def load(info):

    girderRoot = info['serverRoot']
    histomicsRoot = Webroot(_template)
    histomicsRoot.updateHtmlVars(girderRoot.vars)
    histomicsRoot.updateHtmlVars({'title': 'HistomicsTK'})

    info['serverRoot'].histomicstk = histomicsRoot
    info['serverRoot'].girder = girderRoot

    # create root resource for all REST end points of HistomicsTK
    resource = DockerResource('HistomicsTK')
    setattr(info['apiRoot'], resource.resourceName, resource)

    # load docker images from cache
    dockerImageModel = ModelImporter.model('docker_image_model',
                                           'slicer_cli_web')
    dockerCache = dockerImageModel.loadAllImages()

    # generate REST end points for slicer CLIs of each docker image
    genRESTEndPointsForSlicerCLIsInDockerCache(resource, dockerCache)

    # auto-ingest annotations into database when a .anot file is uploaded
    events.bind('data.process', 'HistomicsTK', process_annotations)

    events.bind('jobs.job.update.after', resource.resourceName,
                resource.AddRestEndpoints)
Example #2
0
def load(info):
    # Load the mako template for Minerva and serve it as the root document.
    minerva_mako = os.path.join(os.path.dirname(__file__), "minerva.mako")
    minerva_webroot = Webroot(minerva_mako)
    minerva_webroot.updateHtmlVars(info['serverRoot'].vars)
    minerva_html_vars = {'title': 'Minerva', 'externalJsUrls': []}
    minerva_webroot.updateHtmlVars(minerva_html_vars)

    def add_downstream_plugin_js_urls(downstream_plugin_js_urls):
        """Allow additional external JS resources to be loaded from downstream plugins."""
        minerva_html_vars.setdefault('externalJsUrls', []).extend(downstream_plugin_js_urls.info)
        minerva_webroot.updateHtmlVars(minerva_html_vars)

    events.bind('minerva.additional_js_urls', 'minerva', add_downstream_plugin_js_urls)

    # Move girder app to /girder, serve minerva app from /
    info['serverRoot'], info['serverRoot'].girder = (minerva_webroot,
                                                     info['serverRoot'])
    info['serverRoot'].api = info['serverRoot'].girder.api

    events.bind('model.setting.validate', 'minerva', validate_settings)

    info['apiRoot'].minerva_dataset = dataset.Dataset()
    info['apiRoot'].minerva_session = session.Session()

    info['apiRoot'].minerva_datasets_wms = wms_dataset.WmsDataset()
    info['apiRoot'].minerva_style_wms = wms_styles.Sld()
    info['apiRoot'].minerva_dataset_geojson = geojson_dataset.GeojsonDataset()
    info['apiRoot'].minerva_get_feature_info = feature.FeatureInfo()
    info['apiRoot'].minerva_geocoder = geocoder.Geocoder()
    info['apiRoot'].minerva_postgres_geojson = postgres_geojson.PostgresGeojson()

    info['apiRoot'].gaia_analysis = gaia_analysis.GaiaAnalysis()
    info['apiRoot'].gaia_process = geoprocess.GeoProcess()

    info['serverRoot'].wms_proxy = WmsProxy()

    # If we are started up in testing mode, then serve minerva's sources as well
    # for debugging client tests.
    if '/test' in info['config']:
        info['config']['/src/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/web_external'
        }
        info['config']['/test/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/plugin_tests/client'
        }
Example #3
0
def load(info):
    # Load the mako template for Minerva and serve it as the root document.
    minerva_mako = os.path.join(os.path.dirname(__file__), "minerva.mako")
    minerva_webroot = Webroot(minerva_mako)
    minerva_webroot.updateHtmlVars(info['serverRoot'].vars)
    minerva_webroot.updateHtmlVars({'title': 'Minerva'})

    # Move girder app to /girder, serve minerva app from /
    info['serverRoot'], info['serverRoot'].girder = (minerva_webroot,
                                                     info['serverRoot'])
    info['serverRoot'].api = info['serverRoot'].girder.api

    events.bind('model.setting.validate', 'minerva', validate_settings)

    info['apiRoot'].minerva_dataset = dataset.Dataset()
    info['apiRoot'].minerva_analysis = analysis.Analysis()
    info['apiRoot'].minerva_session = session.Session()
    info['apiRoot'].minerva_dataset_s3 = s3_dataset.S3Dataset()

    info['apiRoot'].minerva_source = source.Source()

    info['apiRoot'].minerva_source_wms = wms_source.WmsSource()
    info['apiRoot'].minerva_source_s3 = s3_source.S3Source()
    info['apiRoot'].minerva_dataset_wms = wms_dataset.WmsDataset()

    info['apiRoot'].minerva_dataset_geojson = geojson_dataset.GeojsonDataset()

    info['apiRoot'].minerva_source_elasticsearch = \
        elasticsearch_source.ElasticsearchSource()
    info['apiRoot'].minerva_query_elasticsearch = \
        elasticsearch_source.ElasticsearchQuery()
    info['apiRoot'].minerva_source_postgres = \
        postgres_source.PostgresSource()
    info['apiRoot'].minerva_source_mongo = mongo_source.MongoSource()
    info['apiRoot'].minerva_dataset_mongo = mongo_dataset.MongoDataset()
    info['serverRoot'].wms_proxy = WmsProxy()

    # If we are started up in testing mode, then serve minerva's sources as well
    # for debugging client tests.
    if '/test' in info['config']:
        info['config']['/src/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/web_external'
        }
        info['config']['/test/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/plugin_tests/client'
        }
Example #4
0
def load(info):

    girderRoot = info['serverRoot']
    histomicsRoot = Webroot(_template)
    histomicsRoot.updateHtmlVars(girderRoot.vars)
    histomicsRoot.updateHtmlVars({'title': 'HistomicsTK'})

    info['serverRoot'].histomicstk = histomicsRoot
    info['serverRoot'].girder = girderRoot

    # create root resource for all REST end points of HistomicsTK
    resource = DockerResource('HistomicsTK')
    setattr(info['apiRoot'], resource.resourceName, resource)

    # load docker images from cache
    dockerImageModel = ModelImporter.model('docker_image_model',
                                           'slicer_cli_web')
    dockerCache = dockerImageModel.loadAllImages()

    # generate REST end points for slicer CLIs of each docker image
    genRESTEndPointsForSlicerCLIsInDockerCache(resource, dockerCache)

    # auto-ingest annotations into database when a .anot file is uploaded
    events.bind('data.process', 'HistomicsTK', process_annotations)

    events.bind('jobs.job.update.after', resource.resourceName,
                resource.AddRestEndpoints)
Example #5
0
def load(info):
    # Load the mako template for Minerva and serve it as the root document.
    minerva_mako = os.path.join(os.path.dirname(__file__), "minerva.mako")
    minerva_webroot = Webroot(minerva_mako)
    minerva_webroot.updateHtmlVars(info['serverRoot'].vars)
    minerva_html_vars = {'title': 'Minerva', 'externalJsUrls': []}
    minerva_webroot.updateHtmlVars(minerva_html_vars)

    def add_downstream_plugin_js_urls(downstream_plugin_js_urls):
        """Allow additional external JS resources to be loaded from downstream plugins."""
        minerva_html_vars.setdefault('externalJsUrls',
                                     []).extend(downstream_plugin_js_urls.info)
        minerva_webroot.updateHtmlVars(minerva_html_vars)

    events.bind('minerva.additional_js_urls', 'minerva',
                add_downstream_plugin_js_urls)

    # Move girder app to /girder, serve minerva app from /
    info['serverRoot'], info['serverRoot'].girder = (minerva_webroot,
                                                     info['serverRoot'])
    info['serverRoot'].api = info['serverRoot'].girder.api

    events.bind('model.setting.validate', 'minerva', validate_settings)

    info['apiRoot'].minerva_dataset = dataset.Dataset()
    info['apiRoot'].minerva_session = session.Session()

    info['apiRoot'].minerva_datasets_wms = wms_dataset.WmsDataset()
    info['apiRoot'].minerva_style_wms = wms_styles.Sld()
    info['apiRoot'].minerva_dataset_geojson = geojson_dataset.GeojsonDataset()
    info['apiRoot'].minerva_get_feature_info = feature.FeatureInfo()
    info['apiRoot'].minerva_geocoder = geocoder.Geocoder()
    info[
        'apiRoot'].minerva_postgres_geojson = postgres_geojson.PostgresGeojson(
        )

    info['apiRoot'].gaia_analysis = gaia_analysis.GaiaAnalysis()
    info['apiRoot'].gaia_process = geoprocess.GeoProcess()

    info['serverRoot'].wms_proxy = WmsProxy()

    # If we are started up in testing mode, then serve minerva's sources as well
    # for debugging client tests.
    if '/test' in info['config']:
        info['config']['/src/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/web_external'
        }
        info['config']['/test/minerva'] = {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': 'plugins/minerva/plugin_tests/client'
        }
Example #6
0
def load(info):
    girderRoot = info['serverRoot']
    histomicsRoot = Webroot(_template)
    histomicsRoot.updateHtmlVars(girderRoot.vars)
    histomicsRoot.updateHtmlVars({'title': 'HistomicsTK'})

    info['serverRoot'].histomicstk = histomicsRoot
    info['serverRoot'].girder = girderRoot

    genRESTEndPointsForSlicerCLIsInDocker(
        info, 'HistomicsTK',
        'dsarchive/histomicstk:v0.1.2'
    )
Example #7
0
def load(info):
    # Load the mako template for Vaui and serve it as the root document.
    mako = os.path.join(os.path.dirname(__file__), "index.mako")
    webroot = Webroot(mako)
    webroot.updateHtmlVars(info['serverRoot'].vars)
    html_vars = {'title': 'Vaui', 'externalJsUrls': []}
    webroot.updateHtmlVars(html_vars)

    # Move girder app to /girder, serve app from /
    info['serverRoot'], info['serverRoot'].girder = (webroot,
                                                     info['serverRoot'])
    info['serverRoot'].api = info['serverRoot'].girder.api

    info['apiRoot'].detection = detection.DetectionResource()
    info['apiRoot'].vaui_annotation = annotation.AnnotationResource()
    info['apiRoot'].activities = activities.ActivitiesResource()
    info['apiRoot'].types = types.TypesResource()
    info['apiRoot'].interpolation = interpolation.Interpolation()
Example #8
0
def load(info):
    flow_mako = os.path.join(os.path.dirname(__file__), "flow.mako")
    flow_webroot = Webroot(flow_mako)
    flow_webroot.updateHtmlVars({'brand': 'Arbor'})

    # @todo somehow the API lives at /api/v1 and /girder/api/v1
    info['serverRoot'], info['serverRoot'].girder = (flow_webroot,
                                                     info['serverRoot'])

    info['serverRoot'].api = info['serverRoot'].girder.api

    staticDir = os.path.join(info['pluginRootDir'], 'static')
    if os.path.isdir(staticDir):
        for path in os.listdir(staticDir):
            if os.path.isdir(os.path.join(staticDir, path)):
                info['config'][str('/' + path)] = {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': os.path.join(staticDir, path),
                    'tools.staticdir.index': 'index.html'
                }

    @access.public
    def flowConvert(itemId, inputType, inputFormat, outputFormat, params):
        itemApi = info['apiRoot'].item

        content = getItemContent(itemId, itemApi)

        asyncResult = getCeleryApp().send_task('girder_worker.convert', [
            inputType, {
                "data": content,
                "format": inputFormat
            }, {
                "format": outputFormat
            }
        ])

        return asyncResult.get()

    flowConvert.description = (
        Description('Convert an item from one format to another').param(
            'itemId', 'ID of the item to be converted').param(
                'inputType', 'The type of the input data').param(
                    'inputFormat', 'The format of the input data').param(
                        'outputFormat', 'The desired output format'))

    @access.public
    def getTaskId(jobId):
        # Get the celery task ID for this job.
        jobApi = info['apiRoot'].job
        job = jobApi.model('job', 'jobs').load(jobId,
                                               user=jobApi.getCurrentUser(),
                                               level=AccessType.READ)
        return job["celeryTaskId"]

    @access.public
    def flowRunStatus(itemId, jobId, params):
        celeryTaskId = getTaskId(jobId)

        # Get the celery result for the corresponding task ID.
        result = AsyncResult(celeryTaskId, backend=getCeleryApp().backend)
        try:
            response = {'status': result.state}
            if result.state == celery.states.FAILURE:
                response['message'] = str(result.result)
            elif result.state == 'PROGRESS':
                response['meta'] = str(result.result)
            return response
        except Exception:
            return {
                'status': 'FAILURE',
                'message': sys.exc_info(),
                'trace': sys.exc_info()[2]
            }

    flowRunStatus.description = (
        Description('Show the status of a flow task').param(
            'jobId', 'The job ID for this task.',
            paramType='path').param('itemId', 'Not used.', paramType='path'))

    @access.public
    def flowRunResult(itemId, jobId, params):
        celeryTaskId = getTaskId(jobId)
        job = AsyncResult(celeryTaskId, backend=getCeleryApp().backend)
        return {'result': job.result}

    flowRunResult.description = (
        Description('Show the final output of a flow task.').param(
            'jobId', 'The job ID for this task.',
            paramType='path').param('itemId', 'Not used.', paramType='path'))

    @access.public
    @rest.boundHandler(info['apiRoot'].item)
    @rest.loadmodel(map={'itemId': 'item'},
                    model='item',
                    level=AccessType.READ)
    def flowRun(self, item, params):
        # Make sure that we have permission to perform this analysis.
        # import pudb
        # pu.db
        user = self.getCurrentUser()

        settings = ModelImporter.model('setting')
        requireAuth = settings.get(FlowPluginSettings.REQUIRE_AUTH, True)

        if requireAuth:
            safeFolders = settings.get(FlowPluginSettings.SAFE_FOLDERS, ())
            fullAccessUsers = settings.get(
                FlowPluginSettings.FULL_ACCESS_USERS, ())
            fullAccessGrps = settings.get(
                FlowPluginSettings.FULL_ACCESS_GROUPS, ())
            userGrps = {str(id) for id in user.get('groups', ())}

            if (str(item['folderId']) not in safeFolders
                    and (not user or user['login'] not in fullAccessUsers)
                    and not userGrps & set(fullAccessGrps)):
                raise AccessException('Unauthorized user.')

        analysis = item.get('meta', {}).get('analysis')

        if type(analysis) is not dict:
            raise rest.RestException(
                'Must specify a valid JSON object as the "analysis" metadata '
                'field on the input item.')
        # Get the analysis parameters (includes inputs & outputs).
        try:
            kwargs = json.load(cherrypy.request.body)
        except ValueError:
            raise rest.RestException(
                'You must pass a valid JSON object in the request body.')

        return runAnalysis(user, analysis, kwargs, item)

    flowRun.description = (
        Description('Run a task specified by item metadata.').param(
            'itemId',
            'The item containing the analysis as metadata.',
            paramType='path').param('kwargs',
                                    'Additional kwargs for the worker task.',
                                    paramType='body'))

    @access.public
    def flowRunOutput(itemId, jobId, params):
        jobApi = info['apiRoot'].job
        celeryTaskId = getTaskId(jobId)
        timeout = 300
        cherrypy.response.headers['Content-Type'] = 'text/event-stream'
        cherrypy.response.headers['Cache-Control'] = 'no-cache'

        def sseMessage(output):
            if type(output) == unicode:
                output = output.encode('utf8')
            return 'event: log\ndata: {}\n\n'.format(output)

        def streamGen():
            start = time.time()
            endtime = None
            oldLog = ''
            while (time.time() - start < timeout
                   and cherrypy.engine.state == cherrypy.engine.states.STARTED
                   and (endtime is None or time.time() < endtime)):
                # Display new log info from this job since the
                # last execution of this loop.
                job = jobApi.model('job',
                                   'jobs').load(jobId,
                                                user=jobApi.getCurrentUser(),
                                                level=AccessType.READ)
                newLog = job['log']
                if newLog != oldLog:
                    start = time.time()
                    logDiff = newLog[newLog.find(oldLog) + len(oldLog):]
                    oldLog = newLog
                    # We send a separate message for each line,
                    # as I discovered that any information after the
                    # first newline was being lost...
                    for line in logDiff.rstrip().split('\n'):
                        yield sseMessage(line)
                if endtime is None:
                    result = AsyncResult(celeryTaskId,
                                         backend=getCeleryApp().backend)
                    if (result.state == celery.states.FAILURE
                            or result.state == celery.states.SUCCESS
                            or result.state == celery.states.REVOKED):
                        # Stop checking for messages in 5 seconds
                        endtime = time.time() + 5
                time.sleep(0.5)

            # Signal the end of the stream
            yield 'event: eof\ndata: null\n\n'

            # One more for good measure - client should not get this
            yield 'event: past-end\ndata: null\n\n'

        return streamGen

    @access.public
    def flowStopRun(jobId, params):
        task = AsyncResult(jobId, backend=getCeleryApp().backend)
        task.revoke(getCeleryApp().broker_connection(), terminate=True)
        return {'status': task.state}

    flowStopRun.description = (
        Description('Stop execution of the specified job').param(
            'jobId', 'The Job ID for this task'))

    info['apiRoot'].flow_validator = Validator(getCeleryApp())
    info['apiRoot'].item.route(
        'POST', ('flow', ':inputType', ':inputFormat', ':outputFormat'),
        flowConvertData)

    info['apiRoot'].item.route('GET', (':itemId', 'flow', ':jobId', 'status'),
                               flowRunStatus)

    info['apiRoot'].item.route('GET', (':itemId', 'flow', ':jobId', 'result'),
                               flowRunResult)

    info['apiRoot'].item.route('POST', (':itemId', 'flow'), flowRun)

    info['apiRoot'].item.route('GET', (':itemId', 'flow', ':jobId', 'output'),
                               flowRunOutput)

    info['apiRoot'].item.route(
        'GET',
        (':itemId', 'flow', ':inputType', ':inputFormat', ':outputFormat'),
        flowConvert)

    info['apiRoot'].item.route('DELETE', (':itemId', 'flow', ':jobId'),
                               flowStopRun)

    events.bind('model.setting.validate', 'flow', validateSettings)
Example #9
0
def load(info):
    flow_mako = os.path.join(os.path.dirname(__file__), "flow.mako")
    flow_webroot = Webroot(flow_mako)
    flow_webroot.updateHtmlVars({
        'brand': 'Arbor'
    })

    # @todo somehow the API lives at /api/v1 and /girder/api/v1
    info['serverRoot'], info['serverRoot'].girder = (flow_webroot,
                                                     info['serverRoot'])

    info['serverRoot'].api = info['serverRoot'].girder.api

    staticDir = os.path.join(info['pluginRootDir'], 'static')
    if os.path.isdir(staticDir):
        for path in os.listdir(staticDir):
            if os.path.isdir(os.path.join(staticDir, path)):
                info['config'][str('/' + path)] = {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': os.path.join(staticDir, path),
                    'tools.staticdir.index': 'index.html'
                }

    @access.public
    def flowConvert(itemId, inputType, inputFormat, outputFormat, params):
        itemApi = info['apiRoot'].item

        content = getItemContent(itemId, itemApi)

        asyncResult = getCeleryApp().send_task('girder_worker.convert', [
            inputType,
            {"data": content, "format": inputFormat},
            {"format": outputFormat}
        ])

        return asyncResult.get()
    flowConvert.description = (
        Description('Convert an item from one format to another')
        .param('itemId', 'ID of the item to be converted')
        .param('inputType', 'The type of the input data')
        .param('inputFormat', 'The format of the input data')
        .param('outputFormat', 'The desired output format'))

    @access.public
    def getTaskId(jobId):
        # Get the celery task ID for this job.
        jobApi = info['apiRoot'].job
        job = jobApi.model('job', 'jobs').load(
            jobId, user=jobApi.getCurrentUser(), level=AccessType.READ)
        return job["celeryTaskId"]

    @access.public
    def flowRunStatus(itemId, jobId, params):
        celeryTaskId = getTaskId(jobId)

        # Get the celery result for the corresponding task ID.
        result = AsyncResult(celeryTaskId, backend=getCeleryApp().backend)
        try:
            response = {'status': result.state}
            if result.state == celery.states.FAILURE:
                response['message'] = str(result.result)
            elif result.state == 'PROGRESS':
                response['meta'] = str(result.result)
            return response
        except Exception:
            return {
                'status': 'FAILURE',
                'message': sys.exc_info(),
                'trace': sys.exc_info()[2]
            }
    flowRunStatus.description = (
        Description('Show the status of a flow task')
        .param('jobId', 'The job ID for this task.', paramType='path')
        .param('itemId', 'Not used.', paramType='path'))

    @access.public
    def flowRunResult(itemId, jobId, params):
        celeryTaskId = getTaskId(jobId)
        job = AsyncResult(celeryTaskId, backend=getCeleryApp().backend)
        return {'result': job.result}
    flowRunResult.description = (
        Description('Show the final output of a flow task.')
        .param('jobId', 'The job ID for this task.', paramType='path')
        .param('itemId', 'Not used.', paramType='path'))

    @access.public
    @rest.boundHandler(info['apiRoot'].item)
    @rest.loadmodel(map={'itemId': 'item'}, model='item',
                    level=AccessType.READ)
    def flowRun(self, item, params):
        # Make sure that we have permission to perform this analysis.
        # import pudb
        # pu.db
        user = self.getCurrentUser()

        settings = ModelImporter.model('setting')
        requireAuth = settings.get(FlowPluginSettings.REQUIRE_AUTH, True)

        if requireAuth:
            safeFolders = settings.get(FlowPluginSettings.SAFE_FOLDERS, ())
            fullAccessUsers = settings.get(FlowPluginSettings.FULL_ACCESS_USERS, ())
            fullAccessGrps = settings.get(FlowPluginSettings.FULL_ACCESS_GROUPS, ())
            userGrps = {str(id) for id in user.get('groups', ())}

            if (str(item['folderId']) not in safeFolders and (
                    not user or user['login'] not in fullAccessUsers) and
                    not userGrps & set(fullAccessGrps)):
                raise AccessException('Unauthorized user.')

        analysis = item.get('meta', {}).get('analysis')

        if type(analysis) is not dict:
            raise rest.RestException(
                'Must specify a valid JSON object as the "analysis" metadata '
                'field on the input item.')
        # Get the analysis parameters (includes inputs & outputs).
        try:
            kwargs = json.load(cherrypy.request.body)
        except ValueError:
            raise rest.RestException(
                'You must pass a valid JSON object in the request body.')

        return runAnalysis(user, analysis, kwargs, item)
    flowRun.description = (
        Description('Run a task specified by item metadata.')
        .param('itemId', 'The item containing the analysis as metadata.',
               paramType='path')
        .param('kwargs', 'Additional kwargs for the worker task.',
               paramType='body'))

    @access.public
    def flowRunOutput(itemId, jobId, params):
        jobApi = info['apiRoot'].job
        celeryTaskId = getTaskId(jobId)
        timeout = 300
        cherrypy.response.headers['Content-Type'] = 'text/event-stream'
        cherrypy.response.headers['Cache-Control'] = 'no-cache'

        def sseMessage(output):
            if type(output) == unicode:
                output = output.encode('utf8')
            return 'event: log\ndata: {}\n\n'.format(output)

        def streamGen():
            start = time.time()
            endtime = None
            oldLog = ''
            while (time.time() - start < timeout and
                   cherrypy.engine.state == cherrypy.engine.states.STARTED and
                   (endtime is None or time.time() < endtime)):
                # Display new log info from this job since the
                # last execution of this loop.
                job = jobApi.model('job', 'jobs').load(
                    jobId,
                    user=jobApi.getCurrentUser(),
                    level=AccessType.READ)
                newLog = job['log']
                if newLog != oldLog:
                    start = time.time()
                    logDiff = newLog[newLog.find(oldLog) + len(oldLog):]
                    oldLog = newLog
                    # We send a separate message for each line,
                    # as I discovered that any information after the
                    # first newline was being lost...
                    for line in logDiff.rstrip().split('\n'):
                        yield sseMessage(line)
                if endtime is None:
                    result = AsyncResult(celeryTaskId,
                                         backend=getCeleryApp().backend)
                    if (result.state == celery.states.FAILURE or
                            result.state == celery.states.SUCCESS or
                            result.state == celery.states.REVOKED):
                        # Stop checking for messages in 5 seconds
                        endtime = time.time() + 5
                time.sleep(0.5)

            # Signal the end of the stream
            yield 'event: eof\ndata: null\n\n'

            # One more for good measure - client should not get this
            yield 'event: past-end\ndata: null\n\n'

        return streamGen

    @access.public
    def flowStopRun(jobId, params):
        task = AsyncResult(jobId, backend=getCeleryApp().backend)
        task.revoke(getCeleryApp().broker_connection(), terminate=True)
        return {'status': task.state}
    flowStopRun.description = (
        Description('Stop execution of the specified job')
        .param('jobId', 'The Job ID for this task'))

    info['apiRoot'].flow_validator = Validator(getCeleryApp())
    info['apiRoot'].item.route(
        'POST',
        ('flow', ':inputType', ':inputFormat', ':outputFormat'),
        flowConvertData)

    info['apiRoot'].item.route(
        'GET',
        (':itemId', 'flow', ':jobId', 'status'),
        flowRunStatus)

    info['apiRoot'].item.route(
        'GET',
        (':itemId', 'flow', ':jobId', 'result'),
        flowRunResult)

    info['apiRoot'].item.route(
        'POST',
        (':itemId', 'flow'),
        flowRun)

    info['apiRoot'].item.route(
        'GET',
        (':itemId', 'flow', ':jobId', 'output'),
        flowRunOutput)

    info['apiRoot'].item.route(
        'GET',
        (':itemId', 'flow', ':inputType', ':inputFormat',
         ':outputFormat'),
        flowConvert)

    info['apiRoot'].item.route(
        'DELETE',
        (':itemId', 'flow', ':jobId'),
        flowStopRun)

    events.bind('model.setting.validate', 'flow', validateSettings)