Source: utils_module.js

/*
* Módulo utils, conjunto de funciones y herrameintas que son utilizadas de forma genérica por vario módulos
*
* @module module_utils
*
*/
var utils_module = (function (verbose) {

    var _VERBOSE = verbose;
    var buckets = 20;
    var deciles = 10;
    var _TYPE_BIO = 0;



    /**
     * Método que procesa la información para los histogramas de epsion y score por especie.
     *
     * @function processDataForFreqSpecie
     * @public
     * 
     * @param {json} data - Colección de datos resultantes del análisis configurado.
     */
    function processDataForFreqSpecie(data) {

        var min_eps = d3.min(data.map(function (d) {
            return parseFloat(d.epsilon);
        }));
        // debug("min_eps: " + min_eps)
        var max_eps = d3.max(data.map(function (d) {
            return parseFloat(d.epsilon);
        }));
        // debug("max_eps: " + max_eps)

        var min_scr = d3.min(data.map(function (d) {
            return parseFloat(d.score);
        }));
        // debug("min_scr: " + min_scr)  
        var max_scr = d3.max(data.map(function (d) {
            return parseFloat(d.score);
        }));
        // debug("max_scr: " + max_scr)


        var beans = d3.range(1, buckets + 1, 1);

        //        var epsRange = d3.scaleQuantile().domain([min_eps, max_eps]).range(beans);
        //        var scrRange = d3.scaleQuantile().domain([min_scr, max_scr]).range(beans);
        var epsRange = d3.scale.quantile().domain([min_eps, max_eps]).range(beans);
        var scrRange = d3.scale.quantile().domain([min_scr, max_scr]).range(beans);

        // debug("epsRange: " + epsRange.invertExtent(1))

        var cross_species = crossfilter(data)
        cross_species.groupAll();

        var eps_dimension = cross_species.dimension(function (d) {
            return parseFloat(d.epsilon);
        });
        var scr_dimension = cross_species.dimension(function (d) {
            return parseFloat(d.score);
        });

        var groupByEpsilon = eps_dimension.group(function (d) {
            // debug("epsRange: " + epsRange(d))
            return epsRange(d)
        });

        var groupByScore = scr_dimension.group(function (d) {
            return scrRange(d)
        });

        var data_eps = groupByEpsilon.top(Infinity);
        data_eps.sort(_compare);
        // debug(data_eps);

        var data_scr = groupByScore.top(Infinity);
        data_scr.sort(_compare);
        // debug(data_scr);

        var data_freq = [];

        data_freq = generateFrequencyBeans(data_eps, epsRange, "_epsilon", data_freq, buckets);
        data_freq = generateFrequencyBeans(data_scr, scrRange, "_score", data_freq, buckets);

        // debug(data_freq);

        return data_freq;
    }
    


    /**
     * Método que procesa la información para el histograma de score por celda.
     *
     * @function processDataForFreqCell
     * @public
     * 
     * @param {json} data - Colección de datos resultantes del análisis configurado.
     */
    function processDataForFreqCell(data) {

        var min_scr = d3.min(data.map(function (d) {
            return parseFloat(d.tscore);
        }));
        // debug("min_score: " + min_scr)
        var max_scr = d3.max(data.map(function (d) {
            return parseFloat(d.tscore);
        }));
        // debug("min_score: " + max_scr)

        var beans = d3.range(1, buckets + 1, 1);

        //        var scrRange = d3.scaleQuantile().domain([min_scr, max_scr]).range(beans);
        var scrRange = d3.scale.quantile().domain([min_scr, max_scr]).range(beans);

        var cross_score = crossfilter(data)
        cross_score.groupAll();

        var scr_dimension = cross_score.dimension(function (d) {
            return parseFloat(d.tscore);
        });

        var groupByScoreCell = scr_dimension.group(function (d) {
            return scrRange(d)
        });

        var score_cell_data = groupByScoreCell.top(Infinity);
        score_cell_data.sort(_compare);
        
        //        console.log(score_cell_data)
        var data_freq = [];

        data_freq = generateFrequencyBeans(score_cell_data, scrRange, "", data_freq, buckets);
        // debug(data_freq)

        return data_freq;


    }


    /**
     * Método que procesa la información para el histograma de score por celda.
     *
     * @function generateFrequencyBeans
     * @public
     * 
     * @param {json} data_bucket - Mapa de datos agrupados por un valor específico.
     * @param {function} funcRange - Función de quantiles con dominio y rango definidos.
     * @param {sting} paramType - Prefijo para diferenciar resultados.
     * @param {json} data_freq - Referencia al arreglo de resultados.
     * @param {array} buckets - Colección con las cubetas donde son divididos los resultados.
     */
    function generateFrequencyBeans(data_bucket, funcRange, paramType, data_freq, buckets) {

        var index = 0;
        var index_bucket = 0;

        while (index_bucket < buckets) {

            const entry = data_bucket[index];

            var bucket = entry["key"];
            var freq = entry["value"];
            var range = funcRange.invertExtent((index_bucket + 1));

            // debug("freq" + paramType+ ": " + freq);
            // debug("bucket" + paramType+ ": " + bucket);
            // debug("index+1" + paramType+ ": " + (index_bucket+1));

            if ((index_bucket + 1) === bucket) {

                if (data_freq[index_bucket] === undefined) {

                    var item = {};

                    item["bucket"] = bucket;
                    item["freq" + paramType] = freq;
                    item["min" + paramType] = parseFloat((range[0]).toFixed(3));
                    item["max" + paramType] = parseFloat((range[1]).toFixed(3));
                    data_freq.push(item);

                } else {
                    data_freq[index_bucket]["freq" + paramType] = freq;
                    data_freq[index_bucket]["min" + paramType] = parseFloat(range[0].toFixed(3));
                    data_freq[index_bucket]["max" + paramType] = parseFloat(range[1].toFixed(3));
                }

                index++;
            } else {

                if (data_freq[index_bucket] === undefined) {

                    var item = {};

                    item["bucket"] = index_bucket + 1;
                    item["freq" + paramType] = 0;
                    item["min" + paramType] = parseFloat((range[0]).toFixed(3));
                    item["max" + paramType] = parseFloat((range[1]).toFixed(3));
                    data_freq.push(item);

                } else {
                    data_freq[index_bucket]["freq" + paramType] = 0;
                    data_freq[index_bucket]["min" + paramType] = parseFloat(range[0].toFixed(3));
                    data_freq[index_bucket]["max" + paramType] = parseFloat(range[1].toFixed(3));
                }

            }

            index_bucket++;
        }

        return data_freq;
    }



    /**
     * Suma el score por cada celda, retorna el score total por cada celda existente en el arreglo
     *
     * @function reduceScoreCell
     * @public
     * 
     */
    function reduceScoreCell(data, val_apriori = 0, numr = 1) {

        var cross_cells = crossfilter(data)

        cross_cells.groupAll();
        var cells_dimension = cross_cells.dimension(function (d) {
            return d.gridid;
        });

        var groupByCell = cells_dimension.group().reduceSum(function (d) {
            return d.tscore;
        });

        var map_cell = groupByCell.top(Infinity);
        
        var cell_score_array = [];
        for (var i = 0; i < map_cell.length; i++) {

            const entry = map_cell[i];

            var tscore = parseFloat(entry["value"]) - (val_apriori*(numr-1))
            var gridid = entry["key"]

            cell_score_array.push({gridid: gridid, tscore: parseFloat(tscore.toFixed(3))})

        }
        
//        console.log(cell_score_array);
        
        return cell_score_array;

    }


    /**
     * Obtiene las celdas de cada especie de la respuesta obtenida. Asigna a cada celda el valor de la especie y finalmente se raliza la sumatoria del score por celda
     *
     * @function processDataForScoreCell
     * @public
     * 
     */
    function processDataForScoreCell(data, apriori, mapa_prob, all_cells = []) {

        _VERBOSE ? console.log("processDataForScoreCell") : _VERBOSE;

        var cells_array = data.map(function (d) {
            return {cells: d.cells, score: parseFloat(d.score)}
        });

        var cells = [];
        cells_array.forEach(function (item, index) {
            item.cells.forEach(function (cell_item, index) {
                cells.push({cell: cell_item, score: item.score})
            })
        })
        // debug(cells)

        var cross_cells = crossfilter(cells)
        cross_cells.groupAll();

        var cells_dimension = cross_cells.dimension(function (d) {
            return d.cell;
        });

        var groupByCell = cells_dimension.group().reduceSum(function (d) {
            return parseFloat(d.score);
        });

        var map_cell = groupByCell.top(Infinity);

        var cell_score_array = [];
        for (var i = 0; i < map_cell.length; i++) {
            const entry = map_cell[i];
            cell_score_array.push({gridid: entry["key"], tscore: parseFloat(entry["value"].toFixed(3))})
        }

        var data_freq = [];

        // debug(cell_score_array)
        return cell_score_array

    }


    function processDataForScoreDecil(data_cell) {

        _VERBOSE ? console.log("processDataForScoreDecil") : _VERBOSE;

        var decile = 10;
        var delta = Math.floor(data_cell.length / decile)

        // debug(data_cell.length)
        // debug(delta)

        data_cell.reverse()
        data_cell.forEach(function (item, index) {
            var dec = Math.floor(index / delta) + 1
            item["decile"] = dec > decile ? decile : dec
            // item["avg"] = 0
        })
        // data_cell.reverse()

        console.log(data_cell)

        var map_groupd = d3.map([]);

        data_cell.forEach(function (row_item, index) {

            if (!map_groupd.has(row_item.decile)) {

                var item = {};

                item.decile = row_item.decile
                item.tscore = row_item.tscore 
                item.max = row_item.tscore
                item.min = row_item.tscore
                item.cont = 1

                map_groupd.set(item.decile, item)

            }
            else {
                
                var item = map_groupd.get(row_item.decile);
                
                item.tscore = item.tscore + row_item.tscore 
                item.max = item.max < row_item.tscore ? row_item.tscore : item.max
                item.min = item.min > row_item.tscore ? row_item.tscore : item.min
                item.cont += 1

                map_groupd.set(item.decile, item)
                
            }

        })

        // console.log(map_groupd)

        // var cross_score = crossfilter(data_cell)
        // cross_score.groupAll()

        // var scr_dimension = cross_score.dimension(function (d) {
        //     return d.decile
        // });

        // var groupByScoreCell = scr_dimension.group().reduce(
        //         function (item, add) {
        //             item.val = item.val + 1
        //             item.sum = item.sum + add.tscore
        //             item.min = item.min < add.tscore ? item.min : add.tscore
        //             item.max = item.max > add.tscore ? item.max : add.tscore
        //             return item
        //         },
        //         function (item, remove) {
        //             item.val = item.val - 1
        //             item.sum = item.sum - remove.tscore
        //             item.min = item.min < add.tscore ? item.min : add.tscore
        //             item.max = item.max > add.tscore ? item.max : add.tscore
        //             return item
        //         },
        //         function () {
        //             return {
        //                 val: 0,
        //                 sum: 0,
        //                 min: Number.MAX_VALUE,
        //                 max: Number.MIN_VALUE
        //             }
        //         }
        // )

        // var score_cell_decil = groupByScoreCell.top(Infinity);
        // score_cell_decil.sort(_compare_desc)
        // // debug(score_cell_decil)

        var score_cell_decil = map_groupd.values()
        var cell_decil_array = []

        console.log(score_cell_decil)
        
        for (var i = 0; i < score_cell_decil.length; i++) {
            
            cell_decil_array.push({
                decil: score_cell_decil[i].decile,
                avg: parseFloat(score_cell_decil[i].tscore / score_cell_decil[i].cont).toFixed(3),
                l_sup: score_cell_decil[i].max,
                l_inf: score_cell_decil[i].min,
                vp: 0,
                fn: 0,
                nulos: 0,
                recall: 0
            })

        }

        console.log(cell_decil_array)

        cell_decil_array.reverse();

        return cell_decil_array;


    }

    function hashCode(str) {
      return str.split('').reduce((prevHash, currVal) =>
        (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
    }


    function processTitleGroup(groupid, tfilters) {

        _VERBOSE ? console.log("processTitleGroup") : _VERBOSE;

        _VERBOSE ? console.log(groupid) : _VERBOSE;
        _VERBOSE ? console.log(tfilters) : _VERBOSE;

        var title_valor = ''

        // debug("groupid: " + groupid)
        // debug(tfilters)

        if (groupid !== undefined) {

            // group_item = 0 ->> root
            if (parseInt(tfilters[0].type) === _TYPE_BIO) {

                title_valor = JSON.stringify(
                        {'title': 'Grupo Bio ' + groupid,
                            'type': tfilters[0].type,
                            'group_item': tfilters[0].group_item,
                            'is_parent': true})
            } else { //if (tfilters[0].type != 0) {


                title_valor = JSON.stringify(
                        {'title': 'Grupo Raster ' + groupid,
                            'type': tfilters[0].type,
                            'group_item': tfilters[0].group_item,
                            'is_parent': true})
            }
            // else { 
            //   title_valor = JSON.stringify(
            //     {'title':'Grupo Topo ' + groupid, 
            //       'type': tfilters[0].type , 
            //       'group_item': tfilters[0].group_item, 
            //       'is_parent':true })
            // }
        } else if (tfilters[0].value) {

            if (parseInt(tfilters[0].type) === _TYPE_BIO) {


                title_valor = JSON.stringify(
                        {'title': tfilters[0].value,
                            'type': tfilters[0].type,
                            'group_item': tfilters[0].group_item,
                            'is_parent': false})
            } else {


                title_valor = JSON.stringify(
                        {'title': tfilters[0].label,
                            'type': tfilters[0].type,
                            'group_item': tfilters[0].group_item,
                            'is_parent': false})
            }
        }

        return JSON.parse(title_valor)
    }


    /**
     * Asigna el decil a cada una de las celdas, filtra las celdas que pertenecen al decil selecionado y obtiene el número de presencias de cada especie por decil
     *
     * @function processDataForScoreDecilTable
     * @public
     * 
     */
    function processDataForScoreDecilTable(data_cell, decil_selected) {

        _VERBOSE ? console.log("processDataForScoreDecilTable") : _VERBOSE;

        var decile = deciles
        var delta = Math.floor(data_cell.length / decile)

        data_cell.reverse()
        data_cell.forEach(function (item, index) {
            var dec = Math.floor(index / delta) + 1
            item["decile"] = dec > decile ? decile : dec
        })
        data_cell.reverse()
        
        // console.log(data_cell)
        console.log(data_cell.length)
        // console.log(data_cell[0].decile)
        // console.log(decil_selected)
        // console.log(data_cell[0].decile === decil_selected)
        // console.log(data_cell[0].decile == decil_selected)
        // console.log(8 == decil_selected)

        var decil_array = data_cell.filter(function(item){
            
          return item.decile == decil_selected  
             
        });

        // console.log(decil_array)
        console.log(decil_array.length)


        var map_spid = d3.map([]);

        // data_cell.forEach(function (row_item, index) {
        decil_array.forEach(function (row_item, index) {

            // if (row_item.decile != decil_selected)
            //     return;

            row_item.species.forEach(function (specie, index) {

                // if (!map_spid.has(specie.spid)) {
                if (!map_spid.has(specie.name)) {
                    var item = {};
                    item.decile = row_item.decile
                    // item.spid = specie.spid
                    item.gridid = row_item.gridid
                    item.score = specie.score
                    item.epsilon = specie.epsilon
                    item.nj = specie.nj
                    item.njd = 1
                    item.name = specie.name
                    // map_spid.set(specie.spid, item)
                    map_spid.set(specie.name, item)
                } else {
                    // var item = map_spid.get(specie.spid);
                    var item = map_spid.get(specie.name);
                    item.njd = item.njd + 1
                    map_spid.set(specie.name, item)
                    // map_spid.set(specie.spid, item)
                }

            })

        })

        return {
            tbl_freq_decil: map_spid.values(), 
            length_decil: decil_array.length,
            decil_array: decil_array
        } 


    }


    /**
     * Obtiene el score por celda. Obtiene la sumatoria de todas las especies que tiene presencia en cada celda.
     *
     * @function processDataForScoreCellTable
     * @public
     * 
     */
    function processDataForScoreCellTable(data) {

        _VERBOSE ? console.log("processDataForScoreCellTable") : _VERBOSE;

        var total_length
        var cells = d3.map([]);


        data.forEach(function (item, index) {

            total_length = item.n

            item.cells.forEach(function (cell_item, index) {
            
                var name = item.reinovalido === "" ? (item.layer + " " + item.tag) : (item.generovalido +" "+item.especieepiteto+" "+item.nombreinfra)
                
                var item_map = {
                    cell: cell_item,
                    score: parseFloat(item.score),
                    epsilon: parseFloat(item.epsilon),
                    nj: parseFloat(item.nj),
                    name: name
                }

                cells.set("" + cell_item + name, item_map)
                
            })
        })

        console.log("cells_values: " + cells.values().length)

        var cross_cells = crossfilter(cells.values())
        cross_cells.groupAll();

        var cells_dimension = cross_cells.dimension(function (d) {
            return d.cell;
        });

        var groupByScoreCell = cells_dimension.group().reduce(
                function (item, add) {
                    item.tscore = item.tscore + add.score
                    // item.spids.push(add.spid)
                    item.epsilons.push(add.epsilon)
                    item.scores.push(add.score)
                    item.njs.push(add.nj)
                    item.names.push(add.name)

                    return item
                },
                function (item, remove) {
                    item.tscore = item.tscore - remove.score

                    var index = item.names.indexOf(remove.name);
                    if (index > -1) {
                        item.epsilons.splice(index, 1);
                        item.scores.splice(index, 1);
                        item.njs.splice(index, 1);
                        item.names.splice(index, 1);
                    }

                    return item
                },
                function () {
                    return {
                        tscore: 0,
                        spids: [],
                        epsilons: [],
                        scores: [],
                        njs: [],
                        names: []

                    }
                }
        )

        // var groupByCell = cells_dimension.group().reduceSum(function(d) { return parseFloat(parseFloat(d.score).toFixed(3)); });
        var map_cell = groupByScoreCell.top(Infinity);

        console.log(map_cell)
        console.log("map_cell: " + map_cell.length)


        // Obtiene un array con gridid, score total (key) y un array con las espcecies en la celda, cada especie con sus valores de epsilon, score y nj

        var cell_score_array = [];
        for (var i = 0; i < map_cell.length; i++) {
            const entry = map_cell[i];
            // var len = entry["value"].spids.length;
            var len = entry["value"].names.length;
            // console.log("len: " + len)

            var item = {};
            item.gridid = entry["key"];
            item.key = parseFloat((entry["value"].tscore).toFixed(3));

            var species = [];
            for (var j = 0; j < len; j++) {

                var specie = {};
                // specie.spid = entry["value"].spids[j];
                specie.epsilon = entry["value"].epsilons[j];
                specie.score = entry["value"].scores[j];
                specie.nj = entry["value"].njs[j];
                specie.name = entry["value"].names[j];

                species.push(specie)
            }
            item.species = species;
            cell_score_array.push(item)

        }
        cell_score_array.sort(_compare_desc);

        // retorna y el total de celdas de la malla
        return {array: cell_score_array, total_length: total_length} 

    }

    function reduceDecilGroups(validatation_data_result){

          var cross_cells = crossfilter(validatation_data_result)
          cross_cells.groupAll();

          var decil_dimension = cross_cells.dimension(function(d) { return d.decil; });
          
          var groupByDecil = decil_dimension.group().reduce(
            function(item,add){
              ++item.count
              
              item.vp += add.vp
              item.fn = item.fn + add.fn
              item.nulo = item.nulo + add.nulo
              item.recall = item.recall + add.recall
              
              return item
            },
            function(item,remove){
              --item.count
              
              item.vp -= remove.vp
              item.fn = item.fn - remove.fn
              item.nulo = item.nulo - remove.nulo
              item.recall = item.recall - remove.recall

              return item
            },
            function(){
              return {
                count: 0,
                vp: 0,
                fn: 0,
                nulo: 0,
                recall: 0
              }
            }
          )

          var reduce_data = groupByDecil.top(Infinity);
          reduce_data.sort(_compare_desc)

          var data_result = []
          for(var i=0; i<reduce_data.length; i++){
              const entry = reduce_data[i];

              data_result.push({
                decil: entry["key"],
                vp: parseFloat((entry["value"].vp / entry["value"].count).toFixed(2)),
                fn: parseFloat((entry["value"].fn / entry["value"].count).toFixed(2)),
                nulo: parseFloat((entry["value"].nulo / entry["value"].count).toFixed(2)),
                recall: parseFloat((entry["value"].recall / entry["value"].count).toFixed(2))
                
              })
          }

          // debug(data_result)

          return data_result

    }


    function _compare(a, b) {
        if (a.key < b.key)
            return -1;
        if (a.key > b.key)
            return 1;
        return 0;
    }

    function _compare_desc(a, b) {

        _VERBOSE ? console.log("_compare_desc") : _VERBOSE;

        if (a.key > b.key)
            return -1;
        if (a.key < b.key)
            return 1;
        return 0;
    }


    function getLinkToken(data_link, type, url_api, url_front) {

        console.log("utils getLinkToken");
        // console.log(data_link)

        $.ajax({
            url: url_api + "/niche/especie/getToken",
            type: 'post',
            data: data_link,
            dataType: "json",
            success: function (resp) {

                var cadena_ini = url_front + '#link/?';
                var tokenlink = resp.data[0].token;

                console.log("token: " + tokenlink);

                $("#modalRegenera").modal();
                $("#lb_enlace").val(cadena_ini + "token=" + tokenlink);

            },
            error: function (jqXHR, textStatus, errorThrown) {
                _VERBOSE ? console.log("error: " + textStatus) : _VERBOSE;

            }
        });

    }


    function genLinkURL() {

        _VERBOSE ? console.log("utils genLinkURL") : _VERBOSE;

        if (_json_config == undefined) {
            return;
        }

//        console.log(_json_config.token);
        var token = _json_config.token;
        getValuesFromToken(token);

    }


    /**
     * Regresa la fecha actual con formato YYYY-MM-DD
     *
     * @function _getDateNow 
     * @private
     */
    function _getDateNow() {
        _VERBOSE ? console.log("_getDateNow") : _VERBOSE;

        var today = new Date();
        var dd = today.getDate();
        var mm = today.getMonth() + 1; //January is 0!
        var yyyy = today.getFullYear();

        if (dd < 10) {
            dd = "0" + dd;
        }
        if (mm < 10) {
            mm = "0" + mm;
        }
        today = yyyy + "-" + mm + "-" + dd;

        return today;
    }


    /**
     * Parsea una URL a un JSON.
     *
     * @function parseURL
     * @private
     * 
     * @param {string} url - URL en formato cadena para ser parseado.
     * 
     */
    function parseURL(url) {
    
        // console.log(url);

        var regex = /[?&]([^=#]+)=([^&#]*)/g, url = url, params = {}, match;
        while (match = regex.exec(url)) {
            params[match[1]] = match[2];
        }
        return params;
    }



    //    No utilizado
    function getQuerystring2(path, key, default_) {

        _VERBOSE ? console.log("getQuerystring2") : _VERBOSE;

        if (default_ == null) {
            default_ = "";
        }

        var search = unescape(path);

        if (search == "") {
            return default_;
        }

        search = search.substr(1);
        var params = search.split("&");

        for (var i = 0; i < params.length; i++) {

            var pairs = params[i].split("=");
            if (pairs[0] == key) {
                return pairs[1];
            }

        }

        return default_;
    }

   


    /**
     * Función que inicializa las variables necesarias para la creación de histogramas.
     *
     * @function startUtilsModule
     * @public
     * 
     */
    function startUtilsModule() {
        _VERBOSE ? console.log("startUtilsModule") : _VERBOSE;
    }


    // Añadir los miembros públicos
    return{
        startUtilsModule: startUtilsModule,
        processDataForScoreCell: processDataForScoreCell,
        processDataForScoreDecil: processDataForScoreDecil,
        processTitleGroup: processTitleGroup,
        processDataForScoreDecilTable: processDataForScoreDecilTable,
        processDataForScoreCellTable: processDataForScoreCellTable,
        processDataForFreqSpecie: processDataForFreqSpecie,
        processDataForFreqCell: processDataForFreqCell,
        reduceScoreCell: reduceScoreCell,
        reduceDecilGroups: reduceDecilGroups,
        hashCode: hashCode,
        getLinkToken: getLinkToken,
        parseURL: parseURL
        // genLinkURL: genLinkURL
        // getValuesFromToken: getValuesFromToken
    }


});