Source code for QueryLayer.Query

from mimetypes import types_map
from UtilityLayer import INPUT
from UtilityLayer import OUTPUT
from UtilityLayer import RUNTIME
from UtilityLayer import EDIT_PATH
from urllib2 import URLError
import logging as log
import numpy as np
import os

[docs]class Query(): """ Describe content of :mod:`AccessLayer` requests Arguments ----------- args: list See :class:`TileQuery` for use of positional arguments keywords: dict Each derived class may use any keyword arguments given \ keys each match a `NAME` of a :class:`NamedStruct`in \ :class:`INPUT`, :class:`RUNTIME`, or :class:`OUTPUT`. Attributes ------------ INPUT: :class:`INPUT` RUNTIME: :class:`RUNTIME` OUTPUT: :class:`OUTPUT` keywords: dict Taken from ``keywords`` argument """ #: default mime type for HTTP response _basic_mime = 'text/plain' def __init__(self,*args,**keywords): self.INPUT = INPUT() self.RUNTIME = RUNTIME() self.OUTPUT = OUTPUT() # Permanently store the keywords self.keywords = {} self.update_keys(keywords)
[docs] def update_keys(self, keywords): """ Update stored keywords Arguments ---------- keywords: dict Updates stored keywords """ self.keywords.update(keywords) # Get method and feature method = self.keywords.get(self.INPUT.METHODS.NAME,'') feature = self.keywords.get(self.INPUT.FEATURES.NAME,'') # Set method and feature self.INPUT.METHODS.VALUE = method self.INPUT.FEATURES.VALUE = feature # Update the linked list of queries self.set_key(self.OUTPUT.INFO, 'QUERY')
[docs] def set_key(self, struct, key, empty=''): """ Copy the key from keywords to the structure Arguments ---------- struct: :class:`NamelessStruct` Where to store a value from :data:`keywords` key: str The key to transfer from :data:`keywords` empty: anything The value if :data:`keywords` has no ``key``\ and the ``key`` has no default in the struct """ # Get field to be set field = struct.get(key, {}) # Get default value from struct default = field.get('VALUE', empty) # Get value from input keywords if exists val = self.keywords.get(field.NAME, default) # Set keyword value to field setattr(field, 'VALUE', val)
@property def path(self): """ return the path to the whole volume Returns ------- str the path value from ``OUTPUT.INFO`` """ return self.OUTPUT.INFO.PATH.VALUE @property def key(self): """ return the key for the database Returns ------- str the path value from ``OUTPUT.INFO`` """ return self.path @property def edit_path(self): """ return the path to edit the volume Returns ------- str the path under the EDIT_PATH root """ rel_path = self.path.lstrip(os.sep) return os.path.join(EDIT_PATH, rel_path) @property def is_websocket(self): """ Checks whether websocket method Returns -------- bool """ methods = self.INPUT.METHODS scope = methods.VALUE.split(':')[0] # Whether the method starts with websocket return (scope == methods.WEBSOCKET.NAME) @property def is_data(self): """ Checks whether the method requests images Returns ------- bool """ image_methods = self.INPUT.METHODS.IMAGE_LIST return self.INPUT.METHODS.VALUE in image_methods @property def is_group(self): """ Checks whether the method requests groups Returns ------- bool """ group_methods = self.INPUT.METHODS.GROUP_LIST return self.INPUT.METHODS.VALUE in group_methods @property def is_dataset(self): """ Checks if requesting dataset info Returns ------- bool """ dataset_list = ['project_info'] return self.INPUT.METHODS.VALUE in dataset_list @property def mime_type(self): """ Gets the mime type for the file_type The mime type is from :data:`INPUT.IMAGE` \ if :meth:`is_data`, or :data:`INPUT.INFO` \ otherwise. Returns -------- str The mime type from ``mimetypes.types_map`` """ info_type = self.INPUT.INFO.FORMAT.VALUE image_type = self.INPUT.IMAGE.FORMAT.VALUE # Use image type for data and info type for info file_type = image_type if self.is_data else info_type # Get the right mime type or use default for this class _basic_mime = self._basic_mime.format(file_type) return types_map.get('.'+file_type, _basic_mime)
[docs] def update_source(self, keywords): """ Set all attribute values to match keywords Arguments ---------- keywords: dict * :class:`RUNTIME` ``.IMAGE.SOURCE.NAME`` (str) -- The subclass of :class:`Datasource` * :class:`RUNTIME` ``.IMAGE.BLOCK.NAME`` (numpy.ndarray) -- 3x1 for any given tile shape * :class:`OUTPUT` ``.INFO.TYPE.NAME`` (str) -- numpy dtype of any given tile * :class:`OUTPUT` ``.INFO.SIZE.NAME`` (numpy.ndarray) -- 3x1 for full volume shape Keyword arguments only for :class:`HDF5` * :class:`RUNTIME` ``.IMAGE.SOURCE.HDF5.OUTER.NAME`` (str) -- The direct filename to an hdf5 file * :class:`RUNTIME` ``.IMAGE.SOURCE.HDF5.INNER.NAME`` (str) -- The dataset in the file with image data Keyword arguments if editable * :class:`RUNTIME` ``.IMAGE.MERGE.NAME`` lil_matrix -- The matrix of merged ids * :class:`RUNTIME` ``.IMAGE.MERGE.NAME`` lil_matrix -- The matrix of split regions """ # take named keywords output = self.OUTPUT.INFO runtime = self.RUNTIME.IMAGE # Get the right kind of datasource and datatype source_val = keywords.get(runtime.SOURCE.NAME) type_val = keywords.get(output.TYPE.NAME) # Get the right blocksize block = keywords.get(runtime.BLOCK.NAME) # Unpack dimensions for full volume full_size = keywords.get(output.SIZE.NAME, [0,0,0]) # Make sure the source and type are valid self.check_list(runtime.SOURCE.LIST, source_val, 'source') self.check_list(output.TYPE.LIST, type_val, 'type') # Make sure the size has a length of 3 self.check_vector(full_size, 'volume', 3) # Make sure all blocks are valid for block_i in block: msg = 'bigger than {}'.format(block_i) # Make sure each blocksize has length 3 self.check_vector(block_i, 'block', 3) # Make sure size is bigger than each blocksize if not np.all(np.uint32(block_i) <= full_size): msg = 'A {} block will not fit in a {} volume' msg = msg.format(block_i, full_size) raise URLError([msg, 503]) # Set all the clean values output.TYPE.VALUE = type_val runtime.SOURCE.VALUE = source_val runtime.BLOCK.VALUE = np.uint32(block) # Set the output size output.SIZE.VALUE = { output.SIZE.Z.NAME: int(full_size[0]), output.SIZE.Y.NAME: int(full_size[1]), output.SIZE.X.NAME: int(full_size[2]) } ############## # Optional keywords for editing merge_field = runtime.MERGE split_field = runtime.SPLIT merge_field.VALUE = keywords.get(merge_field.NAME) split_field.VALUE = keywords.get(split_field.NAME) ############## # Optional keywords by source # HDF5 h5_field = runtime.SOURCE.HDF5 h5_field.VALUE = keywords.get(h5_field.NAME) # Mojo mojo_format = runtime.SOURCE.MOJO.FORMAT mojo_format.VALUE = keywords.get(mojo_format.NAME) # Boss boss_tiles = runtime.SOURCE.BOSS.PATHS boss_tiles.VALUE = keywords.get(boss_tiles.NAME) boss_start = runtime.SOURCE.BOSS.INFO.START boss_start.VALUE = keywords.get(boss_start.NAME)
[docs] def check_list(self, whitelist, value, term): """ Checks that a value is in a given list Arguments ----------- whitelist: list-like The list of desired values value: anything The value should be in the ``whitelist`` term: str The name of the attribute to test """ if not value in whitelist: msg = 'The {} {} is not in {}'.format(term, value, whitelist) raise URLError([msg, 503])
[docs] def check_vector(self, value, term='list', dims=3): """ Checks that a vector has three dimensions Arguments ----------- term: str The name of the attribute to test value: list-like The value should be a list or array dims: int The desired number of dimensions """ try: return int(value[0]) except (TypeError, ValueError, IndexError): msg = 'The {} {} is not a vector' msg = msg.format(term, value) raise URLError([msg, 503]) # Check length if length given if len(value) != dims: msg = 'The {} {} does not have {} dimensions' msg = msg.format(term, value, dims) raise URLError([msg, 503])