1 from operator
import itemgetter
2 from collections
import namedtuple
4 from couchdbkit
.ext
.django
import loading
5 from couchdbkit
import MultipleResultsFound
6 from couchdbkit
import *
9 logger
= logging
.getLogger(__name__
)
12 def get_userdata_database():
13 return loading
.get_db('userdata')
16 class BulkException(Exception):
18 def __init__(self
, errors
):
22 BulkError
= namedtuple('BulkError', 'doc error reason')
25 def __default_reload(db
, obj
):
27 return obj
.__class
__.wrap(doc
)
30 __get_obj
= itemgetter(0)
32 def bulk_save_retry(obj_funs
, db
, reload_f
=__default_reload
):
33 """ Saves multiple documents and retries failed ones
35 Objects to be saved are passed as (obj, mod_f), where obj is the CouchDB
36 and mod_f is the modification function that should be applied to it.
38 If saving a document fails, it is again fetched from the database, the
39 modification function is applied again and saving is retried. """
45 # apply modification function (and keep funs)
46 obj_funs
= [(f(o
), f
) for (o
, f
) in obj_funs
]
48 # filter those with obj None
49 obj_funs
= filter(lambda of
: __get_obj(of
) is not None, obj_funs
)
52 objs
= map(__get_obj
, obj_funs
)
61 except BulkSaveError
as ex
:
64 for res
, (obj
, f
) in zip(ex
.results
, obj_funs
):
65 if res
.get('error', False) == 'conflict':
67 # reload conflicted object
68 obj
= reload_f(db
, obj
)
69 new_obj_funs
.append( (obj
, f
) )
71 elif res
.get('error', False):
72 # don't retry other errors
73 err
= BulkError(obj
, res
['error'], res
.get('reason', None))
76 obj_funs
= new_obj_funs
79 logger
.warn('Errors at bulk-save: %s', errors
)
80 raise BulkException(errors
)
83 def get_single_result(db
, view
, **query_args
):
84 """ return a single CouchDB view result
86 Logs an error if multiple results are returned, and uses the first result.
87 This can happen as CouchDB can not guarantee uniqueness of attributes other
88 than _id. If no result are fetched, None is returned. """
90 r
= db
.view(view
, **query_args
)
98 except MultipleResultsFound
as ex
:
99 logger
.info('Multiple results found in %s with params %s',
101 # use the first result as fallback
104 # we can only set the db if the result has been
105 # wrapped (depending on query_args)
106 if hasattr(result
, 'set_db'):