diff --git a/src/repdav/textgrid_dav_provider.py b/src/repdav/textgrid_dav_provider.py index 557e849e83560ce583fa017e7ecb230c44b46a36..844df4f3499d7108715d9b5a2c58f9fbc18f0137 100644 --- a/src/repdav/textgrid_dav_provider.py +++ b/src/repdav/textgrid_dav_provider.py @@ -2,23 +2,21 @@ """ import io import logging - +import threading from pprint import pformat -from tgclients.crud import TextgridCRUD from tgclients.config import TextgridConfig -from tgclients.metadata import TextgridMetadata +from tgclients.crud import TextgridCRUD from wsgidav.dav_provider import DAVCollection, DAVNonCollection, DAVProvider -from wsgidav.stream_tools import FileLikeQueue from wsgidav.util import join_uri, pop_path +from repdav.stream_tools import FileLikeQueue + from .tgapi import TextgridAuth, TextgridSearch _logger = logging.getLogger(__name__) -# TODO think about caching with the session ID as key - class TextgridRoot(DAVCollection): """Top level collection that incorporates Textgrid projects. @@ -42,20 +40,32 @@ class TextgridRoot(DAVCollection): _logger.debug("Called TextgridRoot.get_member(self, %s).", name) return TextgridProject(join_uri(self.path, name), self.environ) + def support_etag(self): + """Return True, if this resource supports ETags.""" + return False + + def get_etag(self): + """ + See http://www.webdav.org/specs/rfc4918.html#PROPERTY_getetag + + This method SHOULD be implemented, especially by non-collections. + """ + return None + # temporary override for debugging def resolve(self, script_name, path_info): """Return a _DAVResource object for the path (None, if not found). `path_info`: is a URL relative to this object. """ - _logger.debug("Called TextgridRoot.resolve(self, %s, %s).", - script_name, path_info) + _logger.debug( + "Called TextgridRoot.resolve(self, %s, %s).", script_name, path_info + ) if path_info in ("", "/"): return self assert path_info.startswith("/") name, rest = pop_path(path_info) res = self.get_member(name) - _logger.debug("TextgridRoot_NAME: %s, REST: %s, RES: %s", - name, rest, res) + _logger.debug("TextgridRoot_NAME: %s, REST: %s, RES: %s", name, rest, res) if res is None or rest in ("", "/"): return res _logger.debug("RES: %s", res) @@ -64,8 +74,7 @@ class TextgridRoot(DAVCollection): class TextgridProject(DAVCollection): def __init__(self, path, environ): - _logger.debug( - "Called TextgridProject.__init__(self, %s, environ).", path) + _logger.debug("Called TextgridProject.__init__(self, %s, environ).", path) DAVCollection.__init__(self, path, environ) self._sid = environ["wsgidav.auth.user_name"] @@ -88,8 +97,8 @@ class TextgridProject(DAVCollection): # # path resolution has to be rewritten before we can work with resource titles resources = TextgridSearch().get_project_contents( - self._sid, self.path.split("/")[-1]) - #_logger.debug("RESOURCES: %s", resources) + self._sid, self.path.split("/")[-1] + ) return resources.keys() def get_member(self, name): @@ -106,31 +115,44 @@ class TextgridProject(DAVCollection): def copy_move_single(self, dest_path, is_move): pass + def support_etag(self): + """Return True, if this resource supports ETags.""" + return False + + def get_etag(self): + """ + See http://www.webdav.org/specs/rfc4918.html#PROPERTY_getetag + + This method SHOULD be implemented, especially by non-collections. + """ + return None + # temporary override for debugging def resolve(self, script_name, path_info): """Return a _DAVResource object for the path (None, if not found). `path_info`: is a URL relative to this object. """ _logger.debug( - "Called TextgridProject.resolve(self, %s, %s).", script_name, path_info) + "Called TextgridProject.resolve(self, %s, %s).", script_name, path_info + ) if path_info in ("", "/"): return self assert path_info.startswith("/") name, rest = pop_path(path_info) res = self.get_member(name) - _logger.debug( - "TextgridProject_NAME: %s, REST: %s, RES: %s", name, rest, res) + _logger.debug("TextgridProject_NAME: %s, REST: %s, RES: %s", name, rest, res) if res is None or rest in ("", "/"): return res return res.resolve(join_uri(script_name, name), rest) -# TODO: merge TextgridProject with TextgridAggregation and/or derive from a common base class. + +# TODO: merge TextgridProject with TextgridAggregation +# and/or derive from a common base class. class TextgridAggregation(DAVCollection): def __init__(self, path, environ, info): - _logger.debug( - "Called TextgridAggregation.__init__(self, %s, environ).", path) + _logger.debug("Called TextgridAggregation.__init__(self, %s, environ, info).", path) DAVCollection.__init__(self, path, environ) self._sid = environ["wsgidav.auth.user_name"] self._info = info @@ -147,8 +169,9 @@ class TextgridAggregation(DAVCollection): def get_member_names(self): _logger.debug("Called TextgridAggregation.get_member_names(self).") resources = TextgridSearch().get_aggregation_contents( - self._sid, self.path.split("/")[-1]) - #_logger.debug("RESOURCES: %s", resources) + self._sid, self.path.split("/")[-1] + ) + # _logger.debug("RESOURCES: %s", resources) return resources.keys() def get_member(self, name): @@ -165,20 +188,34 @@ class TextgridAggregation(DAVCollection): def copy_move_single(self, dest_path, is_move): pass + def support_etag(self): + """Return True, if this resource supports ETags.""" + return False + + def get_etag(self): + """ + See http://www.webdav.org/specs/rfc4918.html#PROPERTY_getetag + + This method SHOULD be implemented, especially by non-collections. + """ + return None + # temporary override for debugging def resolve(self, script_name, path_info): """Return a _DAVResource object for the path (None, if not found). `path_info`: is a URL relative to this object. """ _logger.debug( - "Called TextgridAggregation.resolve(self, %s, %s).", script_name, path_info) + "Called TextgridAggregation.resolve(self, %s, %s).", script_name, path_info + ) if path_info in ("", "/"): return self assert path_info.startswith("/") name, rest = pop_path(path_info) res = self.get_member(name) _logger.debug( - "TextgridAggregation_NAME: %s, REST: %s, RES: %s", name, rest, res) + "TextgridAggregation_NAME: %s, REST: %s, RES: %s", name, rest, res + ) if res is None or rest in ("", "/"): return res return res.resolve(join_uri(script_name, name), rest) @@ -188,11 +225,12 @@ class TextgridResource(DAVNonCollection): """Non-Aggregation resources.""" def __init__(self, path, environ, info): - _logger.debug( - "Called TextgridResource.__init__(self, %s, environ).", path) + _logger.debug("Called TextgridResource.__init__(self, %s, environ).", path) DAVNonCollection.__init__(self, path, environ) + self._size = environ.get("CONTENT_LENGTH") self._sid = environ["wsgidav.auth.user_name"] self._info = info + self.upload_thread = None def get_content_length(self): _logger.debug("Called TextgridResource.get_content_length(self).") @@ -213,20 +251,43 @@ class TextgridResource(DAVNonCollection): def begin_write(self, content_type=None): _logger.debug( - "Called TextgridResource.begin_write(self, content_type=%s).", content_type) - config = TextgridConfig() + "Called TextgridResource.begin_write(self, content_type=%s).", content_type + ) + + queue = FileLikeQueue(int(self._size)) + config = TextgridConfig("http://textgridlab.org/") crud = TextgridCRUD(config.crud) - queue = FileLikeQueue(max_size=1) - # create metadata or update? - metadata = TextgridMetadata.create( - self.get_content_title(), self.get_content_type()) - crud.update_resource( - self._sid, self.path.split("/")[-1], queue, metadata) + metadata = crud.read_metadata(self.path.split("/")[-1], self._sid).content + + def worker(): + _logger.debug("Called TextgridResource.begin_write.worker().") + crud.update_resource(self._sid, self.path.split("/")[-1], queue, metadata) + + thread = threading.Thread(target=worker) + thread.setDaemon(True) + thread.start() + self.upload_thread = thread return queue def end_write(self, with_errors): _logger.debug( - "Called TextgridResource.end_write(self, with_errors=%s)", with_errors) + "Called TextgridResource.end_write(self, with_errors=%s)", with_errors + ) + if self.upload_thread: + self.upload_thread.join() + self.upload_thread = None + + def support_etag(self): + """Return True, if this resource supports ETags.""" + return False + + def get_etag(self): + """ + See http://www.webdav.org/specs/rfc4918.html#PROPERTY_getetag + + This method SHOULD be implemented, especially by non-collections. + """ + return None # temporary override for debugging def resolve(self, script_name, path_info): @@ -234,7 +295,8 @@ class TextgridResource(DAVNonCollection): `path_info`: is a URL relative to this object. """ _logger.debug( - "Called TextgridResource.resolve(self, %s, %s).", script_name, path_info) + "Called TextgridResource.resolve(self, %s, %s).", script_name, path_info + ) if path_info in ("", "/"): return self return None @@ -244,12 +306,14 @@ class TextgridResource(DAVNonCollection): # TextgridResourceProvider # ============================================================================ class TextgridResourceProvider(DAVProvider): - """DAV provider that serves Textgrid resources. - """ + """DAV provider that serves Textgrid resources.""" def get_resource_inst(self, path, environ): _logger.debug( - "Called TextgridResourceProvider.get_resource_inst(self, %s, %s).", path, pformat(environ)) + "Called TextgridResourceProvider.get_resource_inst(self, %s, %s).", + path, + pformat(environ), + ) self._count_get_resource_inst += 1 root = TextgridRoot("/", environ) # an instance of _DAVResource (i.e. either DAVCollection or DAVNonCollection)