epiphany-extensions r1809 - in trunk: . extensions/epilicious
- From: mtherning svn gnome org
- To: svn-commits-list gnome org
- Subject: epiphany-extensions r1809 - in trunk: . extensions/epilicious
- Date: Wed, 11 Feb 2009 23:10:17 +0000 (UTC)
Author: mtherning
Date: Wed Feb 11 23:10:17 2009
New Revision: 1809
URL: http://svn.gnome.org/viewvc/epiphany-extensions?rev=1809&view=rev
Log:
Adding diigo backend.
Catching up with recent out-of-tree development.
Added:
trunk/extensions/epilicious/DiigoStore.py
trunk/extensions/epilicious/diigo.py
Modified:
trunk/ (props changed)
trunk/extensions/epilicious/BaseStore.py
trunk/extensions/epilicious/DeliciousStore.py
trunk/extensions/epilicious/EpiphanyStore.py
trunk/extensions/epilicious/Makefile.am
trunk/extensions/epilicious/__init__.py
trunk/extensions/epilicious/backend.py
trunk/extensions/epilicious/config.py
trunk/extensions/epilicious/epilicious.py.in
trunk/extensions/epilicious/epilicious.schemas.in
trunk/extensions/epilicious/magnolia.py
Modified: trunk/extensions/epilicious/BaseStore.py
==============================================================================
--- trunk/extensions/epilicious/BaseStore.py (original)
+++ trunk/extensions/epilicious/BaseStore.py Wed Feb 11 23:10:17 2009
@@ -16,9 +16,28 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-class BaseStore:
+class BaseStore(object):
'''The base of all stores. Never to be instantiated.'''
+ def __init__(self):
+ self.store = 1
+ self.fstore = []
+
+ def perform_calls(self):
+ for f, s, args, kwargs in self.fstore:
+ yield f(s, *args, **kwargs)
+ self.fstore = []
+
+ @classmethod
+ def store_call(cls, func):
+ def inner(self, *args, **kwargs):
+ if self.store:
+ self.fstore.append((func, self, args, kwargs))
+ else:
+ func(self, *args, **kwargs)
+
+ return inner
+
def get_snapshot(self):
'''Calculates a snapshot of the store's bookmarks.
Modified: trunk/extensions/epilicious/DeliciousStore.py
==============================================================================
--- trunk/extensions/epilicious/DeliciousStore.py (original)
+++ trunk/extensions/epilicious/DeliciousStore.py Wed Feb 11 23:10:17 2009
@@ -20,24 +20,25 @@
from sets import Set
import libepilicious
from libepilicious.BaseStore import BaseStore
-from libepilicious.backend import create_backend
+from libepilicious.magnolia import Magnolia
class DeliciousStore(BaseStore):
'''The class representing the storage of bookmarks on a backend with a
del.icio.us-like API.'''
- def __init__(self, user, pwd, space_repl, backend):
+ def __init__(self, user, pwd, space_repl):
'''Constructor.
@param user: Delicious username
@param pwd: Delicious password
@param space_repl: Character that replaces space
'''
+ BaseStore.__init__(self)
self.__un = user
self.__pwd = pwd
self.__sr = space_repl
- self.__d = create_backend(backend)(user, pwd)
+ self.__d = Magnolia(user, pwd)
self.__snap_utd = 0
def get_snapshot(self):
@@ -63,6 +64,7 @@
self.__snap_utd = 1
return res
+ @BaseStore.store_call
def url_delete(self, url):
'''Deletes a URL from the storage.
@@ -77,6 +79,7 @@
except:
libepilicious.get_logger().exception('Failed to delete URL %s' % url)
+ @BaseStore.store_call
def url_sync(self, url, desc, to_del, to_add):
'''Synchronises a URL's tags. The URL is added if it doesn't exist.
Added: trunk/extensions/epilicious/DiigoStore.py
==============================================================================
--- (empty file)
+++ trunk/extensions/epilicious/DiigoStore.py Wed Feb 11 23:10:17 2009
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2009 by Magnus Therning
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from sets import Set
+
+import libepilicious
+from libepilicious.BaseStore import BaseStore
+from libepilicious.diigo import Diigo2
+
+class DiigoStore(BaseStore):
+ '''A class representing the storage of bookmarks on a Diigo backend.
+ '''
+
+ def __init__(self, user, pwd, space_repl):
+ BaseStore.__init__(self)
+ self.__un = user
+ self.__pwd = pwd
+ self.__sr = space_repl
+ self.__bkend = Diigo2(user, pwd)
+ self.__snap_utd = False
+ self.__snap = None
+
+ def get_snapshot(self):
+ '''Retrieves a snapshot of bookmarks.
+
+ @note: L{BaseStore.get_snapshot} documents the format of the return
+ value.
+ '''
+ def fixup_bm(b):
+ url = b['url']
+ title = b['title']
+ tags = [t.replace(self.__sr, ' ') for t in b['tags'].split(',')]
+ return (url, [title, tags])
+
+ if self.__snap_utd:
+ return self.__snap
+ bms = self.__bkend.all_bookmarks()
+ self.__snap = dict([fixup_bm(b) for b in bms])
+ self.__snap_utd = True
+ return self.__snap
+
+ @BaseStore.store_call
+ def url_delete(self, url):
+ '''Deletes a URL from storage.
+
+ @param url: The URL to delete
+ '''
+ try:
+ self.__bkend.delete_bookmark(url)
+ self.__snap_utd = False
+ except:
+ print 'DiigoStore: Failed to delete URL %s' % url
+ libepilicious.get_logger().exception('DiigoStore: Failed to delete URL %s' % url)
+
+ @BaseStore.store_call
+ def url_sync(self, url, desc, to_del, to_add):
+ if to_del or to_add:
+ snap = self.get_snapshot()
+ if snap.has_key(url):
+ tags = (Set(snap[url][1]) | to_add) - to_del
+ else:
+ tags = to_add
+ tag_str = ' '.join([t.replace(' ', self.__sr) for t in tags])
+ try:
+ self.__bkend.add_bookmark(url=url, title=desc, tags=tag_str)
+ self.__snap_utd = False
+ except:
+ print 'DiigoStore: Failed to synchronise URL %s' % url
+ libepilicious.get_logger().exception('DiigoStore: Failed to synchronise URL %s' % url)
Modified: trunk/extensions/epilicious/EpiphanyStore.py
==============================================================================
--- trunk/extensions/epilicious/EpiphanyStore.py (original)
+++ trunk/extensions/epilicious/EpiphanyStore.py Wed Feb 11 23:10:17 2009
@@ -25,6 +25,7 @@
class EpiphanyStore(BaseStore):
def __init__(self, keyword = None, exclude = False):
+ BaseStore.__init__(self)
# TODO: add a check for the priority of the keyword and set it to 0, at
# the moment it's not possible since there's no method for it
# (ephy_node_set_property() isn't in the Python API)
@@ -81,6 +82,7 @@
return res
+ @BaseStore.store_call
def url_delete(self, url):
'''Delete a bookmark.
@@ -94,6 +96,7 @@
for kw in self.__bms.get_keywords().get_children():
self.__bms.unset_keyword(kw, bm)
+ @BaseStore.store_call
def url_sync(self, url, desc, to_del, to_add):
'''Synchronise a bookmark.
Modified: trunk/extensions/epilicious/Makefile.am
==============================================================================
--- trunk/extensions/epilicious/Makefile.am (original)
+++ trunk/extensions/epilicious/Makefile.am Wed Feb 11 23:10:17 2009
@@ -6,10 +6,12 @@
epiliciouslib_PYTHON = \
BaseStore.py \
DeliciousStore.py \
+ DiigoStore.py \
EpiphanyStore.py \
__init__.py \
backend.py \
config.py \
+ diigo.py \
magnolia.py \
progress.py
Modified: trunk/extensions/epilicious/__init__.py
==============================================================================
--- trunk/extensions/epilicious/__init__.py (original)
+++ trunk/extensions/epilicious/__init__.py Wed Feb 11 23:10:17 2009
@@ -88,7 +88,7 @@
@param old: The base snapshot
@param remote: The remote snapshot
@param local: The local snapshot
- @param rem_store: The remote storage (L{DeliciousStore})
+ @param rem_store: The remote storage (L{BaseStore})
@param loc_store: The local storage (L{EpiphanyStore})
'''
o = Set(old.keys())
@@ -178,7 +178,7 @@
@param old: The base snapshot
@param remote: The remote snapshot
@param local: The local snapshot
- @param rem_store: The remote storage (L{DeliciousStore})
+ @param rem_store: The remote storage (L{BaseStore})
@param loc_store: The local storage (L{EpiphanyStore})
'''
get_logger().info('Number of urls to sync %i.' % len(curls))
Modified: trunk/extensions/epilicious/backend.py
==============================================================================
--- trunk/extensions/epilicious/backend.py (original)
+++ trunk/extensions/epilicious/backend.py Wed Feb 11 23:10:17 2009
@@ -16,11 +16,13 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-from libepilicious import magnolia
+from libepilicious.DeliciousStore import DeliciousStore
+from libepilicious.DiigoStore import DiigoStore
-backends = { \
- 'ma.gnolia' : magnolia.Magnolia, \
+stores = { \
+ 'ma.gnolia' : DeliciousStore, \
+ 'diigo' : DiigoStore, \
}
-def create_backend(b):
- return backends[b]
+def create_store(b):
+ return stores[b]
Modified: trunk/extensions/epilicious/config.py
==============================================================================
--- trunk/extensions/epilicious/config.py (original)
+++ trunk/extensions/epilicious/config.py Wed Feb 11 23:10:17 2009
@@ -53,7 +53,7 @@
self.rbexclude.set_active(self.client.get_bool(libepilicious.GCONF_EXCL))
# setting up of the combo box is a bit ugly, it's all about DRY
- for k in libepilicious.backend.backends.keys():
+ for k in libepilicious.backend.stores.keys():
self.cbbackend.append_text(k)
self.cbbackend.remove_text(0)
be = self.client.get_string(libepilicious.GCONF_BACK)
Added: trunk/extensions/epilicious/diigo.py
==============================================================================
--- (empty file)
+++ trunk/extensions/epilicious/diigo.py Wed Feb 11 23:10:17 2009
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2009 by Magnus Therning
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import httplib2
+import urllib
+import urllib2
+import simplejson
+import time
+
+# {{{1 Constants
+_REALM = 'Application'
+_BASE_URI = 'http://api2.diigo.com/'
+_API_URI = _BASE_URI
+
+class DiigoError(Exception): # {{{1
+ def __init__(self, status, msg):
+ self.status = status
+ self.msg = msg
+
+ def __str__(self):
+ return 'DiigoError(status=%(status)i,msg=%(msg)s)' % self.__dict__
+
+def _handle_http_error(function): # {{{1
+
+ def decorate(*args, **kwargs):
+ tries = 0
+ while tries < 5:
+ try:
+ return function(*args, **kwargs)
+ except DiigoError, e:
+ if e.status == 503:
+ time.sleep(3 * tries)
+ tries += 1
+ else:
+ raise e
+
+ # It'd be a shame to lose the docstrings, wouldn't it?
+ decorate.__doc__ = function.__doc__
+ return decorate
+
+class Diigo2(object): # {{{1
+ '''Class for accessing Diigo's webservice API version 2.'''
+
+ def __init__(self, user_name, password):
+ self.__user = user_name
+ self.__passwd = password
+
+ @_handle_http_error
+ def __do_request(self, function, method='GET', body=None, **kwargs):
+ params = urllib.urlencode(kwargs)
+ h = httplib2.Http()
+ h.add_credentials(self.__user, self.__passwd)
+ resp, cont = h.request(_API_URI + function + '?' + params, method=method, body=body)
+ if resp['status'] != '200':
+ raise DiigoError(int(resp['status']), function)
+ return simplejson.loads(cont)
+
+ def all_bookmarks(self, users=None, filter='all'):
+ '''Retrieve all bookmarks.
+
+ @param users : limit to specific users (defaults to yourself)
+ @param filter : all/public (default 'all')
+ '''
+ if users:
+ u = users
+ else:
+ u = self.__user
+ res = []
+ idx = 0
+ l = 100
+ while l == 100:
+ tmp = self.__do_request('bookmarks', users=u, filter=filter, start=idx, rows=100)
+ res, l, idx = res + tmp, len(tmp), idx+100
+ return res
+
+ @_handle_http_error
+ def delete_bookmark(self, url):
+ '''Delete a bookmark.
+
+ @param url : the URL
+ '''
+ return self.__do_request('bookmarks', method='DELETE', url=url)
+
+ @_handle_http_error
+ def add_bookmark(self, title, url, desc='', tags='', shared='yes'):
+ '''Add/update a bookmark.
+
+ @param url : the URL
+ @param title : title for the bookmark
+ @param desc : description of the bookmark (default "")
+ @param tags : string of tags for the URL (separated by spaces, default "")
+ @param shared : whether the URL should be shared or not (default "yes")
+ '''
+ # ToDo: diigo seems to barf on 'dangerous' URLs like file://, come up
+ # with a way to deal with it!
+ return self.__do_request('bookmarks', method='POST', body=' ', \
+ title=title, url=url, desc=desc, tags=tags, shared=shared)
Modified: trunk/extensions/epilicious/epilicious.py.in
==============================================================================
--- trunk/extensions/epilicious/epilicious.py.in (original)
+++ trunk/extensions/epilicious/epilicious.py.in Wed Feb 11 23:10:17 2009
@@ -55,7 +55,7 @@
def __init__(self):
self.username = ''
- self.password = ''
+ self.__password = ''
self.backend = ''
self.keyword = ''
self.exclude = False
@@ -86,7 +86,7 @@
def _new_pwd(self, client, *args, **kwargs):
'''Callback to handle the password is modified in GConf.'''
- self.password = client.get_string(libepilicious.GCONF_PWD)
+ self.__password = client.get_string(libepilicious.GCONF_PWD)
def _new_backend(self, client, *args, **kargs):
'''Callback to handle when the backend is modified in GConf.'''
@@ -102,6 +102,11 @@
'''Callback to handle the exclude is modified in GConf.'''
self.exclude = client.get_bool(libepilicious.GCONF_EXCL)
+ # {{{2 Retrieve password out of keyring (in the future)
+ def _get_password(self):
+ # silly implementation for now
+ return self.__password
+
# {{{2 Synchronisation
def _CB_Sync(self, action, window):
# Using gobject.idle_add() together with a generator is much easier than
@@ -134,15 +139,14 @@
from libepilicious.progress import ProgressBar
pbar = ProgressBar()
try:
- from libepilicious.DeliciousStore import DeliciousStore
+ from libepilicious.backend import create_store
from libepilicious.EpiphanyStore import EpiphanyStore
pbar.show()
stepper = pbar.step()
- remote_store = DeliciousStore(user=self.username, \
- pwd=self.password, space_repl=self.space_repl, \
- backend=self.backend)
+ remote_store = create_store(self.backend)(user=self.username, \
+ pwd=self._get_password(), space_repl=self.space_repl)
local_store = EpiphanyStore(keyword=self.keyword, \
exclude=self.exclude)
stepper.next()
@@ -167,7 +171,20 @@
local = local,
rem_store = remote_store,
loc_store = local_store)
- yield True
+ actions = remote_store.perform_calls()
+ try:
+ while 1:
+ actions.next()
+ yield True
+ except StopIteration, e:
+ pass
+ actions = local_store.perform_calls()
+ try:
+ while 1:
+ actions.next()
+ yield True
+ except StopIteration, e:
+ pass
stepper.next()
changed_urls = libepilicious.find_changed_urls(old = old,
@@ -180,7 +197,20 @@
local = local,
rem_store = remote_store,
loc_store = local_store)
- yield True
+ actions = remote_store.perform_calls()
+ try:
+ while 1:
+ actions.next()
+ yield True
+ except StopIteration, e:
+ pass
+ actions = local_store.perform_calls()
+ try:
+ while 1:
+ actions.next()
+ yield True
+ except StopIteration, e:
+ pass
stepper.next()
# Save the current state for future sync
Modified: trunk/extensions/epilicious/epilicious.schemas.in
==============================================================================
--- trunk/extensions/epilicious/epilicious.schemas.in (original)
+++ trunk/extensions/epilicious/epilicious.schemas.in Wed Feb 11 23:10:17 2009
@@ -36,7 +36,7 @@
<locale name="C">
<short>epilicious backend</short>
<long>The backend that epilicious will use for synchronizing
- bookmarks. Allowed value: 'ma.gnolia'</long>
+ bookmarks. Allowed values: 'diigo', 'ma.gnolia'</long>
</locale>
</schema>
Modified: trunk/extensions/epilicious/magnolia.py
==============================================================================
--- trunk/extensions/epilicious/magnolia.py (original)
+++ trunk/extensions/epilicious/magnolia.py Wed Feb 11 23:10:17 2009
@@ -147,7 +147,7 @@
replace: whether an already existing bookmark should be replaced ("yes"
or "no", default "yes")
shared: whether the URL should be shared or not ("yes" or "no", default
- "no")
+ "yes")
Returns 1 on success, 0 on failure.
'''
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]