more CSS cleanup
[mygpo.git] / mygpo / couchdb.py
bloba26dddbf9009a67ba60b2644e0d14c59a832b5fd
1 from operator import itemgetter
2 from collections import namedtuple
4 from couchdbkit import *
6 class BulkException(Exception):
8 def __init__(self, errors):
9 self.errors = errors
12 BulkError = namedtuple('BulkError', 'doc error reason')
15 def __default_reload(db, obj):
16 _id = getattr(obj, '_id', obj.get('_id', None))
18 if isinstance(obj, Document):
19 return obj.__class__.get(_id)
20 else:
21 return db[_id]
24 __get_obj = itemgetter(0)
26 def bulk_save_retry(db, obj_funs, reload_f=__default_reload):
27 """ Saves multiple documents and retries failed ones
29 Objects to be saved are passed as (obj, mod_f), where obj is the CouchDB
30 and mod_f is the modification function that should be applied to it.
32 If saving a document fails, it is again fetched from the database, the
33 modification function is applied again and saving is retried. """
35 errors = []
37 while True:
39 # apply modification function (and keep funs)
40 obj_funs = [(f(o), f) for (o, f) in obj_funs]
42 # filter those with obj None
43 obj_funs = filter(lambda of: __get_obj(of) is not None, obj_funs)
45 # extract objects
46 objs = map(__get_obj, obj_funs)
48 if not objs:
49 return
51 try:
52 db.save_docs(objs)
53 return
55 except BulkSaveError as ex:
57 new_obj_funs = []
58 for res, (obj, f) in zip(ex.results, obj_funs):
59 if res.get('error', False) == 'conflict':
61 # reload conflicted object
62 obj = reload_f(db, obj)
63 new_obj_funs.append( (obj, f) )
65 elif res.get('error', False):
66 # don't retry other errors
67 err = BulkError(obj, res['error'], res.get('reason', None))
68 errors.append(err)
70 obj_funs = new_obj_funs
72 if errors:
73 raise BulkException(errors)