From becf6cb591a1d609a9616defa290e2eb05ed177a Mon Sep 17 00:00:00 2001 From: Sascha Silbe Date: Fri, 10 Jul 2009 17:11:15 +0000 Subject: change to proposed API --- diff --git a/src/carquinyol/datastore.py b/src/carquinyol/datastore.py index f0f51cd..0d81063 100644 --- a/src/carquinyol/datastore.py +++ b/src/carquinyol/datastore.py @@ -25,6 +25,7 @@ import dbus import gobject from sugar import mime +import sugar.datastore from carquinyol import trace from carquinyol import layoutmanager @@ -109,6 +110,7 @@ class DataStore(dbus.service.Object): if not tvids: logging.debug('Finished updating index.') layoutmanager.get_instance().index_updated = True + self.Ready() return False else: return True @@ -131,22 +133,12 @@ class DataStore(dbus.service.Object): byte_arrays=True) def create(self, props, file_path, transfer_ownership, async_cb, async_err_cb): - tree_id = str(uuid.uuid4()) - version_id = str(uuid.uuid4()) # use fake for now - logging.debug('datastore.create %r %r' % (tree_id, version_id)) - - if not props.get('timestamp', ''): - props['timestamp'] = int(time.time()) - - self._metadata_store.store(tree_id, version_id, props) - self._index_store.store(tree_id, version_id, props) - self._file_store.store(tree_id, version_id, file_path, transfer_ownership, - lambda *args: self._create_completion_cb(async_cb, - async_err_cb, - tree_id, - version_id, - *args)) - return (tree_id, version_id) + """ + Short-term compatibility wrapper for save(). + """ + tree_id, child_id = self.save('', '', props, file_path, transfer_ownership) + self.Created(tree_id, child_id) + return (tree_id, child_id) @dbus.service.signal(DS_DBUS_INTERFACE, signature="ss") def Created(self, tree_id, version_id): @@ -171,42 +163,112 @@ class DataStore(dbus.service.Object): @trace(skip_args=[3], skip_kwargs=["props"]) # "preview" metadata will clutter logfile otherwise def update(self, tree_id, version_id, props, file_path, transfer_ownership, async_cb, async_err_cb): - - if not props.get('timestamp', ''): - props['timestamp'] = int(time.time()) - - # TODO: create branch if required (inside some abstraction layer) + """ + Short-term compatibility wrapper for save() / change_metadata(). + """ if file_path : - # only for data updates - props['parent_id'] = version_id - version_id = str(uuid.uuid4()) + tree_id, child_id = self.save(tree_id, version_id, props, file_path, transfer_ownership) + else : + self.change_metadata(tree_id, version_id, props) + child_id = version_id - self._metadata_store.store(tree_id, version_id, props) - self._index_store.store(tree_id, version_id, props) - - self._file_store.store(tree_id, version_id, file_path, transfer_ownership, - lambda *args: self._update_completion_cb(async_cb, - async_err_cb, - tree_id, - version_id, - *args)) - return (tree_id, version_id) + self.Updated(tree_id, child_id) + return (tree_id, child_id) @dbus.service.signal(DS_DBUS_INTERFACE, signature="ss") def Updated(self, tree_id, version_id): pass @dbus.service.method(DS_DBUS_INTERFACE, - in_signature='a{sv}as', + in_signature='ssa{sv}sb', + out_signature='ss', + byte_arrays=True) + @trace(skip_args=[3], skip_kwargs=["metadata"]) # "preview" metadata will clutter logfile otherwise + def save(self, tree_id, parent_id, metadata, path, delete_after): + # TODO: copy docstring from datastore-redesign.html + if (not tree_id) and parent_id : + raise sugar.datastore.InvalidArgumentError("tree_id is empty but parent_id is not") + + if tree_id and not parent_id : + # TODO: check tree_id does not exist yet + pass + + elif parent_id : + # TODO: check parent_id does exist + pass + + if not tree_id : + tree_id = self._gen_uuid() + + child_id = self._gen_uuid() + + # TODO: create branch if required + + metadata['ctime'] = int(time.time()) + metadata['tree_id'] = tree_id + metadata['parent_id'] = parent_id + metadata['version_id'] = child_id + + self._metadata_store.store(tree_id, child_id, metadata) + # TODO: async + self._index_store.store(tree_id, child_id, metadata) + + if (not path) and self._file_store.has_data(tree_id, parent_id) : + # metadata-only update, reuse parent data + path = self._file_store.retrieve(tree_id, parent_id, os.getuid(), "foo") + + if path: + self._file_store.store(tree_id, child_id, path, delete_after, + lambda *args: self._save_completion_cb(tree_id, parent_id, child_id, *args)) + + return (tree_id, child_id) + + @trace() + def _save_completion_cb(self, tree_id, parent_id, child_id, exc=None): + if exc is not None: + logging.error("Error during saving of entry (%r,%r,%r):\n%s" % ( + tree_id, parent_id, child_id, traceback.format_exc(),)) + # FIXME: what to do on error? for the async API we already guaranteed ACID + return + + self.Saved(tree_id, parent_id, child_id) + self._optimizer.optimize(tree_id, child_id) + logger.debug("updated %s %s" % (tree_id, child_id)) + + @dbus.service.signal(DS_DBUS_INTERFACE, signature="sss") + def Saved(self, tree_id, parent_id, child_id): + # TODO: copy docstring from datastore-redesign.html + pass + + @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='ssa{sv}', + out_signature='', + byte_arrays=True) + @trace(skip_args=[3], skip_kwargs=["metadata"]) # "preview" metadata will clutter logfile otherwise + def change_metadata(self, tree_id, version_id, metadata) : + # TODO: copy docstring from datastore-redesign.html + # TODO + pass + + @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='a{sv}a{sv}', out_signature='aa{sv}u') @trace() - def find(self, query, properties): + def find(self, query, options, querystring = None): t = time.time() + # don't expose internal details + # FIXME: short-term compatibility option + #if 'query' in query : + # del query['query'] + + if querystring : + query['query'] = querystring + tvids = None if layoutmanager.get_instance().index_updated: try: - tvids, count = self._index_store.find(query) + tvids, count = self._index_store.find(query, options) except Exception: logging.error('Failed to query index, will rebuild\n%s' \ % traceback.format_exc()) @@ -216,11 +278,11 @@ class DataStore(dbus.service.Object): self._index_store.open_index() self._rebuild_index() - if tvids is None : + if not layoutmanager.get_instance().index_updated: logging.warning('Index updating, returning all entries') tvids = layoutmanager.get_instance().find_all() - if not query.get('all_versions', False) : + if not options.get('all_versions', False) : # only return latest version for each entry tids_vtime = {} for (tree_id, version_id) in tvids : @@ -231,14 +293,14 @@ class DataStore(dbus.service.Object): count = len(tvids) - offset = query.get('offset', 0) - limit = query.get('limit', MAX_QUERY_LIMIT) + offset = options.get('offset', 0) + limit = options.get('limit', MAX_QUERY_LIMIT) tvids = tvids[offset:offset + limit] # logger.debug('tvids=%r' % (tvids,)) entries = [] for (tree_id,version_id) in tvids: - metadata = self._metadata_store.retrieve(tree_id, version_id, properties) + metadata = self._metadata_store.retrieve(tree_id, version_id, options.get('metadata')) entries.append(metadata) logger.debug('find(): %r' % (time.time() - t)) @@ -247,14 +309,40 @@ class DataStore(dbus.service.Object): return entries, count @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='sa{sv}a{sv}', + out_signature='aa{sv}u') + @trace() + def textsearch(self, querystring, query, options) : + return self.find(query, options, querystring = querystring) + + @dbus.service.method(DS_DBUS_INTERFACE, in_signature='ss', out_signature='s', sender_keyword='sender') @trace() def get_filename(self, tree_id, version_id, sender=None): + """ + Short-term compatibility wrapper for get_filename(). + """ + return self.get_data(uid, vid, sender) + + @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='ss', + out_signature='s', + sender_keyword='sender') + @trace() + def get_data(self, tree_id, version_id, sender=None): + # TODO: copy docstring from datastore-redesign.html user_id = dbus.Bus().get_unix_user(sender) - extension = self._get_extension(tree_id,version_id) - return self._file_store.retrieve(tree_id, version_id, user_id, extension) + extension = self._get_extension(tree_id, version_id) + # TODO: async + path = self._file_store.retrieve(tree_id, version_id, user_id, extension) + self.GotData(sender, tree_id, version_id, path) + return path + + @dbus.service.signal(DS_DBUS_INTERFACE, signature="ssss") + def GotData(self, sender, tree_id, version_id, path) : + pass def _get_extension(self, tree_id, version_id): mime_type = self._metadata_store.get_property(tree_id, version_id, 'mime_type') @@ -274,7 +362,18 @@ class DataStore(dbus.service.Object): in_signature='sa{sv}', out_signature='as') def get_uniquevaluesfor(self, propertyname, query=None): - if propertyname != 'bundle_id': + """ + Short-term compatibility wrapper for find_unique_values(). + """ + return self.find_unique_values(query, propertyname) + + @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='sa{sv}', + out_signature='as') + def find_unique_values(self, query, metadata_name): + # TODO: copy docstring + # TODO: support for arbitrary metadata names and query + if metadata_name != 'bundle_id': raise ValueError('Only ''bundle_id'' is a supported property name') if query: raise ValueError('The query parameter is not supported') @@ -314,3 +413,16 @@ class DataStore(dbus.service.Object): def Stopped(self): pass + @dbus.service.method(DS_DBUS_INTERFACE, + in_signature='', + out_signature='b') + def check_ready(self): + return layoutmanager.get_instance().index_updated + + @dbus.service.signal(DS_DBUS_INTERFACE) + def Ready(self): + pass + + def _gen_uuid(self) : + return str(uuid.uuid4()) + diff --git a/src/carquinyol/filestore.py b/src/carquinyol/filestore.py index d34e3b1..eebec8c 100644 --- a/src/carquinyol/filestore.py +++ b/src/carquinyol/filestore.py @@ -75,6 +75,11 @@ class FileStore(object): async_copy = AsyncCopy(file_path, destination_path, completion_cb) async_copy.start() + def has_data(self, tree_id, version_id) : + dir_path = layoutmanager.get_instance().get_entry_path(tree_id, version_id) + file_path = os.path.join(dir_path, 'data') + return os.path.exists(file_path) + def retrieve(self, tree_id, version_id, user_id, extension): """Place the file associated to a given entry into a directory where the user can read it. The caller is reponsible for deleting this file. diff --git a/src/carquinyol/indexstore.py b/src/carquinyol/indexstore.py index 3a00c25..387f7bb 100644 --- a/src/carquinyol/indexstore.py +++ b/src/carquinyol/indexstore.py @@ -127,12 +127,12 @@ class IndexStore(object): text += value return text - def find(self, query): + def find(self, query, options): enquire = Enquire(self._database) - offset = query.pop('offset', 0) - limit = query.pop('limit', MAX_QUERY_LIMIT) - all_versions = query.pop('all_versions', False) + offset = options.pop('offset', 0) + limit = options.pop('limit', MAX_QUERY_LIMIT) + all_versions = options.pop('all_versions', False) enquire.set_query(self._parse_query(query)) -- cgit v0.9.1