def _getTile(self, item, z, x, y, imageArgs): """ Get an large image tile. :param item: the item to get a tile from. :param z: tile layer number (0 is the most zoomed-out). .param x: the X coordinate of the tile (0 is the left side). .param y: the Y coordinate of the tile (0 is the top). :param imageArgs: additional arguments to use when fetching image data. :return: a function that returns the raw image data. """ try: x, y, z = int(x), int(y), int(z) except ValueError: raise RestException('x, y, and z must be integers', code=400) if x < 0 or y < 0 or z < 0: raise RestException('x, y, and z must be positive integers', code=400) try: tileData, tileMime = self.imageItemModel.getTile( item, x, y, z, **imageArgs) except TileGeneralException as e: raise RestException(e.message, code=404) setResponseHeader('Content-Type', tileMime) setRawResponse() return tileData
def thumbnail(self, segmentation, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') # TODO: convert this to make Segmentation use an AccessControlMixin image = Image().load( segmentation['imageId'], level=AccessType.READ, user=self.getCurrentUser(), exc=True) width = int(params.get('width', 256)) thumbnailImageStream = Segmentation().boundaryThumbnail(segmentation, image, width) if thumbnailImageStream is None: raise RestException('This segmentation is failed, and thus has no thumbnail.', code=410) thumbnailImageData = thumbnailImageStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_segmentation_thumbnail.jpg' % image['name'] if contentDisp == 'inline': setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(thumbnailImageData)) return thumbnailImageData
def getAnnotationMarkupMask(self, annotation, featureId, params): self._ensureMarkup(annotation, featureId) markupFile = Annotation().getMarkupFile(annotation, featureId) if markupFile: return File().download(markupFile, headers=True, contentDisposition='inline') else: image = Image().load(annotation['imageId'], force=True, exc=True) markupMask = numpy.zeros( ( image['meta']['acquisition']['pixelsY'], image['meta']['acquisition']['pixelsX'] ), dtype=numpy.uint8 ) markupMaskEncodedStream = ScikitSegmentationHelper.writeImage(markupMask, 'png') markupMaskEncodedData = markupMaskEncodedStream.getvalue() setRawResponse() setResponseHeader('Content-Type', 'image/png') contentName = 'annotation_%s_%s.png' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',') ) setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) setResponseHeader('Content-Length', len(markupMaskEncodedData)) return markupMaskEncodedData
def getAnnotationMarkupRendered(self, annotation, featureId, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') self._ensureMarkup(annotation, featureId) renderData = Annotation().renderMarkup(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage(renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = 'annotation_%s_%s.jpg' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',') ) if contentDisp == 'inline': setResponseHeader( 'Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader( 'Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(renderEncodedData)) return renderEncodedData
def getTilesRegion(self, item, params): params = self._parseParams(params, True, [ ('left', float, 'region', 'left'), ('top', float, 'region', 'top'), ('right', float, 'region', 'right'), ('bottom', float, 'region', 'bottom'), ('regionWidth', float, 'region', 'width'), ('regionHeight', float, 'region', 'height'), ('units', str, 'region', 'units'), ('width', int, 'output', 'maxWidth'), ('height', int, 'output', 'maxHeight'), ('magnification', float, 'scale', 'magnification'), ('mm_x', float, 'scale', 'mm_x'), ('mm_y', float, 'scale', 'mm_y'), ('exact', bool, 'scale', 'exact'), ('encoding', str), ('jpegQuality', int), ('jpegSubsampling', int), ]) try: regionData, regionMime = self.imageItemModel.getRegion( item, **params) except TileGeneralException as e: raise RestException(e.message) except ValueError as e: raise RestException('Value Error: %s' % e.message) setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def testAnalysisXmlDetection(self, params): """Return the nuclei detection XML spec as a test case.""" xml_file = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', 'histomicstk', 'cli', 'NucleiDetection', 'NucleiDetection.xml')) with open(xml_file) as f: xml = f.read() setResponseHeader('Content-Type', 'application/xml') setRawResponse() return xml
def testAnalysisXmlFeatures(self, params): """Return the nuclei feature classification XML spec as a test case.""" xml_file = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', 'server', 'ComputeNucleiFeatures', 'ComputeNucleiFeatures.xml')) with open(xml_file) as f: xml = f.read() setResponseHeader('Content-Type', 'application/xml') setRawResponse() return xml
def thumbnail(self, image, params): width = int(params.get('width', 256)) height = int(params.get('height', 256)) thumbData, thumbMime = ImageItem().getThumbnail(image, width=width, height=height) # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', thumbMime) return thumbData
def _forward(self): """ Forward a request to the same path with a slash added. """ result = '\n'.join([ '<html>', '<head><title>301 Moved Permanently</title></head>', '<body>', '<center><h1>301 Moved Permanently</h1></center>', '</body>', '</html>']) setResponseHeader('Location', cherrypy.request.path_info + '/') setResponseHeader('Content-Type', 'text/html') setRawResponse() cherrypy.response.status = 301 return result.encode('utf8')
def downloadKeyFile(self, file, algo): self._validateAlgo(algo) if algo not in file: raise RestException('This file does not have the %s hash computed.' % algo) keyFileBody = '%s\n' % file[algo] name = '.'.join((file['name'], algo)) setResponseHeader('Content-Length', len(keyFileBody)) setResponseHeader('Content-Type', 'text/plain') setContentDisposition(name) setRawResponse() return keyFileBody
def downloadKeyFile(self, file, algo, params): self._validateAlgo(algo) if algo not in file: raise RestException('This file does not have the %s hash computed.' % algo) hash = file[algo] name = '.'.join((file['name'], algo)) setResponseHeader('Content-Length', len(hash)) setResponseHeader('Content-Type', 'text/plain') setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % name) setRawResponse() return hash
def _getHdf5Dataset(self, item): try: setResponseHeader('Content-Type', 'image/png') setRawResponse() hdf5Path = [i for i in item['meta'] if 'hdf5Path' in i.keys()][0]['hdf5Path'] pathInHdf5 = [i for i in item['meta'] if 'pathInHdf5' in i.keys()][0]['pathInHdf5'] figure = render_hdf5_dataset(hdf5Path, pathInHdf5) buf = BytesIO() figure.savefig(buf, format='png') return b64encode(buf.getvalue()) except: pass
def getTile(self, image, z, x, y, params): try: x, y, z = int(x), int(y), int(z) except ValueError: raise RestException('x, y, and z must be integers') if x < 0 or y < 0 or z < 0: raise RestException('x, y, and z must be positive integers') try: tileData, tileMime = ImageItem().getTile(image, x, y, z) except TileGeneralException as e: raise RestException(e.message, code=404) setResponseHeader('Content-Type', tileMime) setRawResponse() return tileData
def downloadKeyFile(self, file, algo): self._validateAlgo(algo) if algo not in file: raise RestException('This file does not have the %s hash computed.' % algo) hash = file[algo] name = '.'.join((file['name'], algo)) setResponseHeader('Content-Length', len(hash)) setResponseHeader('Content-Type', 'text/plain') setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % name) setRawResponse() return hash
def getDZITile(self, item, level, xandy, params): _adjustParams(params) tilesize = int(params.get('tilesize', 256)) if tilesize & (tilesize - 1): raise RestException('Invalid tilesize', code=400) overlap = int(params.get('overlap', 0)) if overlap < 0: raise RestException('Invalid overlap', code=400) x, y = [int(xy) for xy in xandy.split('.')[0].split('_')] _handleETag('getDZITile', item, level, xandy, params) metadata = self.imageItemModel.getMetadata(item, **params) level = int(level) maxlevel = int(math.ceil(math.log(max( metadata['sizeX'], metadata['sizeY'])) / math.log(2))) if level < 1 or level > maxlevel: raise RestException('level must be between 1 and the image scale', code=400) lfactor = 2 ** (maxlevel - level) region = { 'left': (x * tilesize - overlap) * lfactor, 'top': (y * tilesize - overlap) * lfactor, 'right': ((x + 1) * tilesize + overlap) * lfactor, 'bottom': ((y + 1) * tilesize + overlap) * lfactor, } width = height = tilesize + overlap * 2 if region['left'] < 0: width += int(region['left'] / lfactor) region['left'] = 0 if region['top'] < 0: height += int(region['top'] / lfactor) region['top'] = 0 if region['left'] >= metadata['sizeX']: raise RestException('x is outside layer', code=400) if region['top'] >= metadata['sizeY']: raise RestException('y is outside layer', code=400) if region['left'] < metadata['sizeX'] and region['right'] > metadata['sizeX']: region['right'] = metadata['sizeX'] width = int(math.ceil(float(region['right'] - region['left']) / lfactor)) if region['top'] < metadata['sizeY'] and region['bottom'] > metadata['sizeY']: region['bottom'] = metadata['sizeY'] height = int(math.ceil(float(region['bottom'] - region['top']) / lfactor)) regionData, regionMime = self.imageItemModel.getRegion( item, region=region, output=dict(maxWidth=width, maxHeight=height), **params) setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def renderAnnotation(self, annotation, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in { 'inline', 'attachment' }: raise ValidationException( 'Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') self.requireParams(['featureId'], params) featureId = params['featureId'] study = Study().load(annotation['meta']['studyId'], force=True, exc=True) featureset = Study().getFeatureset(study) if not any(featureId == feature['id'] for feature in featureset['localFeatures']): raise ValidationException('Invalid featureId.', 'featureId') if Annotation().getState(annotation) != Study().State.COMPLETE: raise RestException('Only complete annotations can be rendered.') renderData = Annotation().renderAnnotation(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage( renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_%s_annotation.jpg' % ( annotation['_id'], featureId.replace('/', ',') # TODO: replace with a better character ) if contentDisp == 'inline': setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(renderEncodedData)) return renderEncodedData
def renderAnnotation(self, annotation, params): Study = self.model('study', 'isic_archive') Annotation = self.model('annotation', 'isic_archive') contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') self.requireParams(['featureId'], params) featureId = params['featureId'] study = Study.load(annotation['meta']['studyId'], force=True, exc=True) featureset = Study.getFeatureset(study) if not any(featureId == feature['id'] for feature in featureset['localFeatures']): raise ValidationException('Invalid featureId.', 'featureId') if Annotation.getState(annotation) != Study.State.COMPLETE: raise RestException('Only complete annotations can be rendered.') renderData = Annotation.renderAnnotation(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage(renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_%s_annotation.jpg' % ( annotation['_id'], featureId.replace('/', ',') # TODO: replace with a better character ) if contentDisp == 'inline': setResponseHeader( 'Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader( 'Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(renderEncodedData)) return renderEncodedData
def pathRedirect(self, path): user = self.getCurrentUser() # Find the longest path that is a valid resource used = len(path) while used: try: resource = path_util.lookUpPath('/'.join(path[:used]), user)['document'] break except path_util.ResourcePathNotFound: if used == 1: raise used -= 1 path = [resource['_modelType'], str(resource['_id'])] + list(path[used:]) path_info = ('/'.join(cherrypy.request.path_info.split('/')[:2] + path)) # This locates the redirected handler cherrypy.request.get_resource(path_info) result = cherrypy.request.handler() setRawResponse() return result
def getTilesThumbnail(self, item, params): params = self._parseParams(params, True, [ ('width', int), ('height', int), ('jpegQuality', int), ('jpegSubsampling', int), ('encoding', str), ]) try: result = self.imageItemModel.getThumbnail(item, **params) except TileGeneralException as e: raise RestException(e.message) except ValueError as e: raise RestException('Value Error: %s' % e.message) if not isinstance(result, tuple): return result thumbData, thumbMime = result setResponseHeader('Content-Type', thumbMime) setRawResponse() return thumbData
def processTask(self, params=None): """ Based on the process name in the URL and JSON in the request body, create & send a WPS request and pass on the response. """ json_body = self.getBodyJson() process = json.loads(json.dumps(json_body), object_hook=deserialize) # assume output is GeoJSON or GeoTIFF process.compute() if process.output.default_output == gaia.formats.PANDAS: result = json.loads(process.output.read(format=gaia.formats.JSON)) else: result = json.loads(process.output.read()) if not isinstance(result, dict): setRawResponse(True) cherrypy.response.headers['Content-Type'] = 'image/tiff' return result
def processTask(self, params): """ Based on the process name in the URL and JSON in the request body, create & send a WPS request and pass on the response. """ json_body = self.getBodyJson() process = json.loads(json.dumps(json_body), object_hook=deserialize) # assume output is GeoJSON or GeoTIFF process.compute() if process.output.default_output == gaia.formats.PANDAS: result = json.loads(process.output.read(format=gaia.formats.JSON)) else: result = json.loads(process.output.read()) if not isinstance(result, dict): setRawResponse(True) cherrypy.response.headers['Content-Type'] = 'image/tiff' return result
def getTilesRegion(self, item, params): _adjustParams(params) params = self._parseParams(params, True, [ ('left', float, 'region', 'left'), ('top', float, 'region', 'top'), ('right', float, 'region', 'right'), ('bottom', float, 'region', 'bottom'), ('regionWidth', float, 'region', 'width'), ('regionHeight', float, 'region', 'height'), ('units', str, 'region', 'units'), ('unitsWH', str, 'region', 'unitsWH'), ('width', int, 'output', 'maxWidth'), ('height', int, 'output', 'maxHeight'), ('fill', str), ('magnification', float, 'scale', 'magnification'), ('mm_x', float, 'scale', 'mm_x'), ('mm_y', float, 'scale', 'mm_y'), ('exact', bool, 'scale', 'exact'), ('frame', int), ('encoding', str), ('jpegQuality', int), ('jpegSubsampling', int), ('tiffCompression', str), ('style', str), ('resample', 'boolOrInt'), ('contentDisposition', str), ]) _handleETag('getTilesRegion', item, params) try: regionData, regionMime = self.imageItemModel.getRegion( item, **params) except TileGeneralException as e: raise RestException(e.args[0]) except ValueError as e: raise RestException('Value Error: %s' % e.args[0]) self._setContentDisposition( item, params.get('contentDisposition'), regionMime, 'region') setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def thumbnail(self, segmentation, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in { 'inline', 'attachment' }: raise ValidationException( 'Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') # TODO: convert this to make Segmentation use an AccessControlMixin image = Image().load(segmentation['imageId'], level=AccessType.READ, user=self.getCurrentUser(), exc=True) width = int(params.get('width', 256)) thumbnailImageStream = Segmentation().boundaryThumbnail( segmentation, image, width) if thumbnailImageStream is None: raise RestException( 'This segmentation is failed, and thus has no thumbnail.', code=410) thumbnailImageData = thumbnailImageStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_segmentation_thumbnail.jpg' % image['name'] if contentDisp == 'inline': setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(thumbnailImageData)) return thumbnailImageData
def _getTile(self, item, z, x, y, imageArgs, mayRedirect=False): """ Get an large image tile. :param item: the item to get a tile from. :param z: tile layer number (0 is the most zoomed-out). .param x: the X coordinate of the tile (0 is the left side). .param y: the Y coordinate of the tile (0 is the top). :param imageArgs: additional arguments to use when fetching image data. :param mayRedirect: if True or one of 'any', 'encoding', or 'exact', allow return a response whcih may be a redirect. :return: a function that returns the raw image data. """ try: x, y, z = int(x), int(y), int(z) except ValueError: raise RestException('x, y, and z must be integers', code=400) if x < 0 or y < 0 or z < 0: raise RestException('x, y, and z must be positive integers', code=400) result = self.imageItemModel._tileFromHash(item, x, y, z, mayRedirect=mayRedirect, **imageArgs) if result is not None: tileData, tileMime = result else: try: tileData, tileMime = self.imageItemModel.getTile( item, x, y, z, mayRedirect=mayRedirect, **imageArgs) except TileGeneralException as e: raise RestException(e.args[0], code=404) setResponseHeader('Content-Type', tileMime) setRawResponse() return tileData
def getItemXML(self, item): setResponseHeader('Content-Type', 'application/xml') setRawResponse() return CLIItem(item).xml
def getXMLSpecHandler(self, *args, **kwargs): setResponseHeader('Content-Type', 'application/xml') setRawResponse() return str_xml
def getTilesRegion(self, item, params): _adjustParams(params) params = self._parseParams(params, True, [ ('left', float, 'region', 'left'), ('top', float, 'region', 'top'), ('right', float, 'region', 'right'), ('bottom', float, 'region', 'bottom'), ('regionWidth', float, 'region', 'width'), ('regionHeight', float, 'region', 'height'), ('units', str, 'region', 'units'), ('unitsWH', str, 'region', 'unitsWH'), ('width', int, 'output', 'maxWidth'), ('height', int, 'output', 'maxHeight'), ('fill', str), ('magnification', float, 'scale', 'magnification'), ('mm_x', float, 'scale', 'mm_x'), ('mm_y', float, 'scale', 'mm_y'), ('exact', bool, 'scale', 'exact'), ('frame', int), ('encoding', str), ('jpegQuality', int), ('jpegSubsampling', int), ('tiffCompression', str), ('style', str), ('resample', 'boolOrInt'), ('contentDisposition', str), ('contentDispositionFileName', str) ]) _handleETag('getTilesRegion', item, params) setResponseTimeLimit(86400) try: regionData, regionMime = self.imageItemModel.getRegion( item, **params) except TileGeneralException as e: raise RestException(e.args[0]) except ValueError as e: raise RestException('Value Error: %s' % e.args[0]) subname = str(int(params.get('region')['left'])) + ',' + str(int(params.get('region')['top'])) self._setContentDisposition( item, params.get('contentDisposition'), regionMime, subname, params.get('contentDispositionFilename')) setResponseHeader('Content-Type', regionMime) if isinstance(regionData, pathlib.Path): BUF_SIZE = 65536 def stream(): try: with regionData.open('rb') as f: while True: data = f.read(BUF_SIZE) if not data: break yield data finally: regionData.unlink() return stream setRawResponse() return regionData