diff --git a/src/repdav/textgrid_named_dav_provider.py b/src/repdav/textgrid_named_dav_provider.py new file mode 100644 index 0000000000000000000000000000000000000000..92fd92a79552bcddf5658dfb5903bc4d841088f6 --- /dev/null +++ b/src/repdav/textgrid_named_dav_provider.py @@ -0,0 +1,154 @@ +import io +import logging +import threading +from pprint import pformat + +from tgclients.config import TextgridConfig +from tgclients.crud import TextgridCRUD +from tgclients.metadata import TextgridMetadata +from wsgidav.util import join_uri + +from repdav.stream_tools import FileLikeQueue +from repdav.textgrid_dav_provider import (TextgridAggregation, TextgridProject, + TextgridResource, + TextgridResourceProvider, + TextgridRoot) + +_logger = logging.getLogger(__name__) + +# ============================================================================ +# TextgridNamedResourceProvider +# ============================================================================ +class TextgridNamedResourceProvider(TextgridResourceProvider): + """DAV provider that serves Textgrid resources.""" + + def get_resource_inst(self, path, environ): + _logger.debug( + "Called TextgridNamedResourceProvider.get_resource_inst(self, %s, %s).", + path, + pformat(environ), + ) + self._count_get_resource_inst += 1 + root = TextgridNamedRoot("/", environ) + # an instance of _DAVResource (i.e. either DAVCollection or DAVNonCollection) + return root.resolve("/", path) + + +class TextgridNamedRoot(TextgridRoot): + """Top level collection that incorporates Textgrid projects. + + This is implemented as READ-ONLY as projects may not be created with webDAV. + """ + + def __init__(self, path, environ): + super().__init__(path, environ) + + def get_member(self, name): + _logger.debug("Called TextgridRoot.get_member(self, %s).", name) + return TextgridNamedProject(join_uri(self.path, name), self.environ) + +class TextgridNamedProject(TextgridProject): + def __init__(self, path, environ): + _logger.debug("Called TextgridNamedProject.__init__(self, %s, environ).", path) + super().__init__(path, environ) + self._tgmeta = TextgridMetadata() + + def get_member_names(self): + _logger.debug("Called TextgridNamedProject.get_member_names(self).") + # names = [] + # the item keys are textgrid uris but we want the resource titles only + # for _, member_dict in self._resources.items(): + # names.append(member_dict.get("title")) + # return names + # + # path resolution has to be rewritten before we can work with resource titles + response = self._tgsearch.list_project_root(self.path.split("/")[-1], self._sid) + names = [] + for result in response.result: + names.append(self._tgmeta.filename_from_metadata(result)) + return names + + def get_member(self, name): + _logger.debug("Called TextgridNamedProject.get_member(self, %s).", name) + tguri = 'textgrid:' + self._tgmeta.id_from_filename(name) + response = self._tgsearch.info(tguri, self._sid) + info = { + name: { + "title": response.result[0].object_value.generic.provided.title, + "format": response.result[0].object_value.generic.provided.format, + "extent": response.result[0].object_value.generic.generated.extent, + } + } + _logger.info("INFO: %s", info) + if "aggregation" in info[name]["format"]: + return TextgridNamedAggregation(join_uri(self.path, name), self.environ, info, self._tgmeta) + return TextgridNamedResource(join_uri(self.path, name), self.environ, info, self._tgmeta) + +class TextgridNamedAggregation(TextgridAggregation): + def __init__(self, path, environ, info, tgmeta): + _logger.debug( + "Called TextgridNamedAggregation.__init__(self, %s, environ, info).", path + ) + super().__init__(path, environ, info) + self._tgmeta = tgmeta + + def get_member_names(self): + _logger.debug("Called TextgridNamedAggregation.get_member_names(self).") + tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1]) + response = self._tgsearch.list_aggregation(tguri, self._sid) + names = [] + for result in response.result: + names.append(self._tgmeta.filename_from_metadata(result)) + return names + + def get_member(self, name): + _logger.debug("Called TextgridNamedAggregation.get_member(self, %s).", name) + tguri = 'textgrid:' + self._tgmeta.id_from_filename(name) + response = self._tgsearch.info(tguri, self._sid) + info = { + name: { + "title": response.result[0].object_value.generic.provided.title, + "format": response.result[0].object_value.generic.provided.format, + "extent": response.result[0].object_value.generic.generated.extent, + } + } + _logger.info("INFO: %s", info) + if "aggregation" in info[name]["format"]: + return TextgridNamedAggregation(join_uri(self.path, name), self.environ, info, self._tgmeta) + return TextgridNamedResource(join_uri(self.path, name), self.environ, info, self._tgmeta) + +class TextgridNamedResource(TextgridResource): + """Non-Aggregation resources.""" + + def __init__(self, path, environ, info, tgmeta): + _logger.debug("Called TextgridNamedResource.__init__(self, %s, environ).", path) + super().__init__(path, environ, info) + self._tgmeta = tgmeta + config = TextgridConfig() + self._crud = TextgridCRUD(config.crud) + + def get_content(self): + _logger.debug("Called TextgridNamedResource.get_content(self) with path:", self.path) + tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1]) + + _logger.debug("crud-read: " + tguri) + return io.BytesIO(self._crud.read_data(tguri, self._sid).content) + + def begin_write(self, content_type=None): + _logger.debug( + "Called TextgridResource.begin_write(self, content_type=%s).", content_type + ) + + queue = FileLikeQueue(int(self._size)) + tguri = 'textgrid:' + self._tgmeta.id_from_filename(self.path.split("/")[-1]) + metadata = self._crud.read_metadata(tguri, self._sid).content + + def worker(): + _logger.debug("Called TextgridResource.begin_write.worker().") + self._crud.update_resource(self._sid, tguri, queue, metadata) + + thread = threading.Thread(target=worker) + thread.setDaemon(True) + thread.start() + self.upload_thread = thread + return queue