source : toolsService.js

(function (angular) {
    'use strict';
    /**
     * @memberof spApp
     * @ngdoc service
     * @name ToolsService
     * @description
     *   List of spatial-hub tools. Includes client side tools with names matching Tool*Service. Includes
     * tools from spatial-service/capabilities.
     */
    angular.module('tools-service', [])
        .factory('ToolsService', ['$injector', '$q', '$http', '$timeout', 'MapService', 'LayersService', 'LoggerService', 'LayoutService',
            function ($injector, $q, $http, $timeout, MapService, LayersService, LoggerService, LayoutService) {
                var cap = $SH.layersServiceCapabilities;
                var viewConfig = $SH.viewConfig;
                var localToolServices = {};
                var promises = [];
                initLocalTools();
                var _httpDescription = function (method, httpconfig) {
                    if (httpconfig === undefined) {
                        httpconfig = {};
                    }
                    httpconfig.service = 'ToolsService';
                    httpconfig.method = method;
                    return httpconfig;
                };
                /*
                uiScope is ToolCtrl
                 */
                function executeRemote(uiScope, inputs) {
                    var m = {};
                    m['input'] = inputs;
                    m['name'] = uiScope.toolName;
                    var url = $SH.baseUrl + '/portal/postTask?sessionId=' + $SH.sessionId;
                    $http.post(url, m, _httpDescription('executeRemote')).then(function (response) {
                        uiScope.externalTaskId = response.data.id;
                        uiScope.statusUrl = LayersService.url() + '/tasks/status/' + response.data.id;
                        // Create log entry for an external tool when the task is created with the taskId
                        LoggerService.log('Tool', uiScope.toolName, '{ "taskId": "' + uiScope.externalTaskId + '"}')
                        $timeout(function () {
                            _checkStatus(uiScope);
                        }, 5000)
                    });
                    return $q.when(false)
                }
                /*
                uiScope is ToolCtrl
                 */
                function _checkStatus(uiScope) {
                    if (uiScope.cancelled) {
                        return;
                    }
                    return $http.get(uiScope.statusUrl + "?last=" + uiScope.last, _httpDescription('checkStatus')).then(function (response) {
                        uiScope.status = response.data.message;
                        var keys = [];
                        var k;
                        for (k in response.data.history) {
                            if (response.data.history.hasOwnProperty(k)) {
                                keys.push(k)
                            }
                        }
                        keys.sort();
                        for (k in keys) {
                            uiScope.log[keys[k]] = response.data.history[keys[k]];
                            uiScope.logText = response.data.history[keys[k]] + '\r\n' + uiScope.logText;
                            uiScope.last = keys[k]
                        }
                        if (response.data.status < 2) {
                            uiScope.checkStatusTimeout = $timeout(function () {
                                _checkStatus(uiScope);
                            }, 5000)
                        } else if (response.data.status === 2) {
                            uiScope.status = 'cancelled';
                            uiScope.finished = true
                        } else if (response.data.status === 3) {
                            uiScope.status = 'error';
                            uiScope.finished = true
                        } else if (response.data.status === 4) {
                            if (response.data.output === undefined) {
                                uiScope.status = 'error';
                                uiScope.finished = true
                            } else {
                                uiScope.status = 'successful';
                                uiScope.finishedData = response.data;
                                return _executeResult(uiScope)
                            }
                        }
                    })
                }
                /**
                 * Operate on tool output uiScope.finishedData.output.
                 *
                 * Match output file:
                 * *.zip - Automatically initiate download when uiScope.spec.download==true
                 * *.html - Assigned to metadataUrl. LayersServiceUrl metadataUrls are opened in an iframe, others in a new tab
                 * *.csv - Opened with CsvCtrl only when there is no *.html
                 * *.tif - Add to the map as a new environmental layer
                 *
                 * Match output name:
                 * "area" - Add a new area to the map
                 * "species" - Add a new species layer to the map
                 * "nextprocess" - Initiate a new tool
                 *
                 * @param uiScope
                 * @returns {Promise<any>}
                 * @private
                 */
                function _executeResult(uiScope) {
                    var layers = [];
                    var nextprocess;
                    for (k in uiScope.finishedData.output) {
                        if (uiScope.finishedData.output.hasOwnProperty(k)) {
                            var d = uiScope.finishedData.output[k];
                            if (d.file && d.file.match(/\.zip$/g) != null) {
                                var filename = uiScope.toolName + " (" + (uiScope.taskId || uiScope.externalTaskId) + ").zip";
                                uiScope.downloadUrl = LayersService.url() + '/tasks/output/' + uiScope.finishedData.id + '/' + encodeURI(filename) + '?filename=' + d.file;
                                if (uiScope.downloadImmediately && uiScope.spec.download !== false) {
                                    Util.download(uiScope.downloadUrl, d.file);
                                }
                            } else if (d.downloadUrl) {
                                uiScope.downloadUrl = d.downloadUrl;
                                // look for "filename=" at the end of the url
                                var filename = 'file'
                                var match = d.downloadUrl.match('filename=.*$')
                                if (match && match.length > 0) {
                                    filename = match[0].replace('filename=', '')
                                } else {
                                    // remove params and use end of url
                                    match = d.downloadUrl.match('([^/\\?]*)(\\?.*)?$')
                                    if (match && match.length > 1) {
                                        filename = match[1]
                                    }
                                }
                                if (uiScope.downloadImmediately && uiScope.spec.download !== false) {
                                    Util.download(uiScope.downloadUrl, filename);
                                }
                            }
                        }
                    }
                    var csvFile = null;
                    var csvUrl = null;
                    for (k in uiScope.finishedData.output) {
                        if (uiScope.finishedData.output.hasOwnProperty(k)) {
                            var d = uiScope.finishedData.output[k];
                            if (d.openUrl) {
                                uiScope.metadataUrl = d.openUrl
                            } else if (d.file && d.file.match(/\.zip$/g) != null) {
                                //processed earlier
                            } else if (d.file && d.file.match(/\.html$/g) != null) {
                                uiScope.metadataUrl = LayersService.url() + '/tasks/output/' + uiScope.finishedData.id + '/' + d.file
                            } else if (d.file && d.file.match(/\.csv/g) != null) {
                                //can only display one csv file
                                csvUrl = LayersService.url() + '/tasks/output/' + uiScope.finishedData.id + '/' + d.file;
                                csvFile = d.file;
                            } else if (d.file && d.file.match(/\.tif$/g) != null) {
                                var name = d.file.replace('/layer/', '').replace('.tif', '');
                                layers.push({
                                    id: name,
                                    displaypath: $SH.geoserverUrl + '/wms?layers=ALA:' + name,
                                    type: 'e',
                                    name: name,
                                    displayname: name,
                                    layer: {
                                        id: name,
                                        displaypath: $SH.geoserverUrl + '/wms?layers=ALA:' + name,
                                        type: 'e',
                                        name: name,
                                        displayname: name
                                    }
                                });
                            } else if (d.name === 'area') {
                                if (d.file.indexOf("{") === 0) {
                                    // parse JSON response
                                    // ENVELOPE is the only output of this type
                                    var json = JSON.parse(d.file);
                                    layers.push({
                                        id: json.id,
                                        displaypath: json.wmsurl,
                                        type: 'envelope',
                                        layertype: 'area',
                                        q: json.q,
                                        name: json.name,
                                        area_km: json.area_km,
                                        bbox: json.bbox,
                                        wkt: json.bbox,
                                        pid: 'ENVELOPE' + json.id
                                    });
                                } else {
                                    //might be an area pid
                                    promises.push(LayersService.getObject(d.file).then(function (data) {
                                        data.data.layertype = 'area';
                                        data.data.log = false // The task is logged, no need to log adding the layer
                                        return MapService.add(data.data)
                                    }))
                                }
                            } else if (d.name === 'species') {
                                var q = jQuery.parseJSON(d.file);
                                if (!q.qid) q.qid = q.q;
                                q.opacity = 60;
                                q.scatterplotDataUrl = uiScope.downloadUrl;
                                q.log = false // The task is logged, no need to log adding the layer
                                promises.push(MapService.add(q))
                            } else if (d.name === 'nextprocess') {
                                var nextinput = jQuery.parseJSON(d.file);
                                // format 'nextprocess' output for LayoutService.openModal
                                nextprocess = {
                                    processName: nextinput.process,
                                    overrideValues: {}
                                };
                                nextprocess.overrideValues[nextinput.process] = {input: nextinput.input};
                            }
                        }
                    }
                    if (layers.length > 0) {
                        $.each(layers, function () {
                            var layer = this;
                            if (uiScope.metadataUrl !== null) layer.metadataUrl = uiScope.metadataUrl;
                            layer.name = uiScope.toolName + " (" + layer.name + ")";
                            layer.log = false // The task is logged, no need to log adding the layer
                            promises.push(MapService.add(layer));
                        })
                    }
                    if (uiScope.metadataUrl !== null) {
                        uiScope.openUrl(uiScope.metadataUrl);
                    } else if (csvUrl !== null) {
                        $http.get(csvUrl, _httpDescription('getCsv')).then(function (data) {
                            var columnOrder = uiScope.spec.output.columnOrder;
                            if (!columnOrder) columnOrder = [];
                            LayoutService.openModal('csv', {
                                title: uiScope.toolName + " (" + csvFile + ")",
                                csv: data.data,
                                columnOrder: columnOrder,
                                info: '',
                                filename: csvFile,
                                display: {size: 'full'}
                            }, false)
                        });
                    }
                    return $q.all(promises).then(function () {
                        uiScope.finished = true;
                        uiScope.$close();
                        if (nextprocess) {
                            $timeout(function () {
                                LayoutService.openModal('tool', nextprocess, false);
                            }, 0)
                        }
                    })
                }
                function registerService(toolName, service) {
                    localToolServices[toolName] = service;
                    cap[toolName] = service.spec
                }
                function executeLocal(uiScope, toolName, inputs) {
                    // Create log entry for a local tool when the task is finished with the input data
                    LoggerService.log('Tool', uiScope.toolName, inputs)
                    var result = localToolServices[toolName].execute(inputs)
                    if (result && result.then) {
                        result.then(function (response) {
                            uiScope.finishedData = response;
                            _executeResult(uiScope);
                            uiScope.$close();
                        })
                    } else {
                        uiScope.$close();
                    }
                }
                function refreshLocal(uiScope, toolName, inputs) {
                    if (typeof localToolServices[toolName].refresh === "function")
                        localToolServices[toolName].refresh(inputs, uiScope.spec)
                }
                function initLocalTools() {
                    //inject all Tools into ToolsService
                    $.each(spApp.requires, function (x) {
                        var v = spApp.requires[x];
                        if (v.match(/-service$/g) != null && v.match(/^tool-/g) != null) {
                            var name = v.replace(/-.|^./g, function (match) {
                                return match.toUpperCase().replace('-', '')
                            });
                            var tool = $injector.get(name);
                            //is this a valid tool service?
                            if (tool && tool.spec && tool.execute) {
                                registerService(name, tool);
                            }
                        }
                    });
                }
                return {
                    /**
                     * Initialize client side tool
                     * @memberof ToolsService
                     * @param {string} name of client side tool
                     * @return {Promise}
                     */
                    init: function (toolName) {
                        if (localToolServices[toolName] && localToolServices[toolName].init) {
                            localToolServices[toolName].init();
                        }
                        return $q.when($SH.layersServiceCapabilities);
                    },
                    /**
                     * Test if a tool name is a client side tool
                     * @memberof ToolsService
                     * @param {string} name of tool
                     * @return {boolean} true when it is a client side tool
                     */
                    isLocalTask: function (toolName) {
                        return localToolServices[toolName] !== undefined;
                    },
                    /**
                     * Test if a tool name is a client side or remote tool
                     * @memberof ToolsService
                     * @param {string} name of tool
                     * @return {boolean} true when it is a client side or remote tool
                     */
                    isTool: function (toolName) {
                        return localToolServices[toolName] !== undefined || cap[toolName] !== undefined;
                    },
                    /**
                     * Run a tool
                     * @memberof ToolsService
                     * @param {Scope} interface scope
                     * @param {string} tool name
                     * @param {Map} input parameters
                     * @return {Promise(Boolean)} true the tool is successful
                     */
                    execute: function (uiScope, toolName, inputs) {
                        if (localToolServices[toolName]) {
                            return executeLocal(uiScope, toolName, inputs)
                        } else {
                            return executeRemote(uiScope, inputs)
                        }
                    },
                    /**
                     * Run a tool
                     * @memberof ToolsService
                     * @param {Scope} interface scope
                     * @param {string} tool name
                     * @param {Map} input parameters
                     * @return {Promise(Boolean)} true the tool is successful
                     */
                    refresh: function (uiScope, toolName, inputs) {
                        if (localToolServices[toolName]) {
                            refreshLocal(uiScope, toolName, inputs)
                        }
                    },
                    /**
                     * Get tool capabilities (info including inputs/outputs)
                     * @memberof ToolsService
                     * @param {string} tool name
                     * @return {Map} capabilities information
                     */
                    getCap: function (toolName) {
                        return cap[toolName]
                    },
                    /**
                     * Get spatial-hub view config for a tool. View config overrides capabilities to control display
                     * and defaults.
                     * @memberof ToolsService
                     * @param {string} tool name
                     * @return {Map} view config including capabilities override information
                     */
                    getViewConfig: function (toolName) {
                        return viewConfig[toolName]
                    },
                    /**
                     * Begin tool status monitoring.
                     *
                     * Monitoring will repeat until execution is finished.
                     *
                     * When successful #executeResult is called.
                     *
                     * @memberof ToolsService
                     * @param {Scope} interface scope
                     */
                    checkStatus: function (uiScope) {
                        _checkStatus(uiScope)
                    },
                    /**
                     * Process tool output. Does download, display and/or add to the map as defined by
                     * capabilities and view config.
                     *
                     * @memberof ToolsService
                     * @param {Scope} interface scope
                     */
                    executeResult: function (uiScope) {
                        _executeResult(uiScope)
                    }
                }
            }])
}(angular));