All files / universal/gpii/node_modules/canopyMatchMaker/src CanopyMatchMakerUtilities.js

98.84% Statements 85/86
97.37% Branches 37/38
100% Functions 13/13
98.8% Lines 82/83

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174                                    3x 3x   3x   3x 721x 721x 7835x 7835x   721x 721x     3x 882x 882x 2882x 2882x 299x   2583x 2583x 583x                 3x 182x 182x 729x   182x             3x 180x 719x   180x 180x 719x               3x 178x 178x     178x   178x   178x   178x 178x 715x   178x     3x     180x 1731x 1731x     180x 719x 719x       719x 719x 7817x 7817x 2069x 2069x     719x   277x 92x   185x 185x 185x 185x 185x         442x 15x   427x 427x     180x             3x 15614x 15614x 33758x 33758x 33758x 13722x     1892x     3x   3x 296x   296x 46x   250x         3x 13056x    
/*!
GPII Canopy Matchmaker
 
Copyright 2012 OCAD University
Copyright 2012 Antranig Basman
 
Licensed under the New BSD license. You may not use this file except in
compliance with this License.
 
The research leading to these results has received funding from the European Union's
Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016.
 
You may obtain a copy of the License at
https://github.com/GPII/universal/blob/master/LICENSE.txt
*/
 
"use strict";
 
var fluid = fluid || require("infusion");
var gpii = gpii || fluid.registerNamespace("gpii");
 
fluid.registerNamespace("gpii.canopyMatchMaker.utils");
 
gpii.canopyMatchMaker.utils.computeFitness = function (leaves, solution, ontologicalMetricFunction) {
    var vector = [];
    for (var i = 0; i < leaves.length; ++i) {
        var leaf = leaves[i];
        vector[i] = ontologicalMetricFunction(leaf, solution);
    }
    vector = vector.sort(gpii.canopyMatchMaker.utils.sortDescending);
    return vector;
};
 
gpii.canopyMatchMaker.utils.compareFitness = function (solA, solB) {
    var i = 0, fitA = solA.fitness, fitB = solB.fitness;
    for (; ; ++i) {
        var endA = i === fitA.length, endB = i === fitB.length;
        if (endA || endB) {
            return endA && endB ? 0 : (endB ? -1 : 1);
        }
        var diff = fitB[i] - fitA[i];
        if (diff !== 0) {
            return diff;
        }
    }
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.sortSolutions = function (solutions) {
    var solArr = [];
    for (var i in solutions) {
        solArr.push(solutions[i]);
    }
    return solArr.sort(gpii.canopyMatchMaker.utils.compareFitness);
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.rankSolutions = function (leaves, solrecs, ontologicalMetricFunction) {
    fluid.each(solrecs, function (solrec) {
        solrec.fitness = gpii.canopyMatchMaker.utils.computeFitness(leaves, solrec.skeleton, ontologicalMetricFunction);
    });
    var solArr = gpii.canopyMatchMaker.utils.sortSolutions(solrecs);
    return fluid.transform(solArr, function (value) {
        return value.index;
    });
};
 
/*
 * @leaves {object}
 * @solrecs {object} map of solutions keyed by solution id.
 */
gpii.canopyMatchMaker.utils.disposeStrategy = function (leaves, solrecs, data, contextId, ontologyMetadata) {
    var ontologicalMetricName = fluid.get(ontologyMetadata, "ontologicalMetricFunction") || "gpii.canopyMatchMaker.utils.prefixLength";
    var ontologicalMetricFunction = fluid.getGlobalValue(ontologicalMetricName);
    // if we already have priority for some solutions, accept these and reject any applications
    // of same type.
    gpii.matchMakerFramework.utils.disposeFromPriority(solrecs, data, contextId);
    // rank remaining solutions based on fit to preferences
    var ranked = gpii.canopyMatchMaker.utils.rankSolutions(leaves, solrecs, ontologicalMetricFunction);
    // dispose based on canopy
    var disposed = gpii.canopyMatchMaker.utils.disposeFromCanopy(leaves, ranked, solrecs, data, contextId, ontologicalMetricFunction);
    // re-key by solution id:
    var togo = {};
    fluid.each(disposed, function (val) {
        togo[val.index] = val;
    });
    return togo;
};
 
gpii.canopyMatchMaker.utils.disposeFromCanopy = function (leaves, ranked, solrecs, data, contextId, ontologicalMetricFunction) {
    // Set default canopy to negative leaf depth. To raise the canopy, a preference needs to
    // match at least one level into the leaf.
    var canopy = fluid.transform(leaves, function (leaf) {
        var leafDepth = fluid.pathUtil.parseEL(leaf).length;
        return -leafDepth;
    });
 
    for (var i = 0; i < ranked.length; ++i) {
        var sol = solrecs[ranked[i]];
        Iif (sol.disposition === "reject") {
            continue;
        }
 
        var inCanopy = false;
        for (var j = 0; j < leaves.length; ++j) {
            var depth = ontologicalMetricFunction(leaves[j], sol.skeleton);
            if (depth > canopy[j]) {
                inCanopy = true;
                canopy[j] = depth;
            }
        }
        if (inCanopy) {
            // if solution is already disposed, don't redo it. Previous steps make sure canopy is raised accordingly
            if (sol.disposition !== undefined) {
                continue;
            }
            var prefActive = gpii.matchMakerFramework.utils.getActiveValueFromEnabledTerms(data.specialPreferences[contextId] || {}, data.specialCapabilities[sol.index], data.solutionsRegistryEntries[sol.index], sol.index);
            sol.disposition = "accept";
            sol.active = (prefActive !== undefined) ? prefActive : true;
            sol.dispositionReason = "Was the solution of this type that best fit user preferences";
            gpii.matchMakerFramework.utils.rejectFromTypes(data.solutionTypes[sol.index],
                data.solutionTypeMapping, solrecs, "Another solution (" + sol.index +
                ") covering more preferences was found found.");
        } else {
            // if solution is already disposed, don't redo it. Previous steps make sure canopy is raised accordingly
            if (sol.disposition !== undefined) {
                continue;
            }
            sol.disposition = "reject";
            sol.dispositionReason = "Was not in canopy.";
        }
    }
    return solrecs;
};
 
/** Returns a non-positive number indicating by how many path segments the supplied
 * path fails to index correctly into the supplied model. A return value of 0
 * indicates that the path indexes fully
 */
gpii.canopyMatchMaker.utils.prefixLength = function (path, model) {
    var segs = fluid.pathUtil.parseEL(path);
    for (var i = 0; i < segs.length; ++i) {
        var seg = segs[i];
        model = model[seg];
        if (model === undefined) {
            return i - segs.length;
        }
    }
    return 0;
};
 
fluid.registerNamespace("gpii.canopyMatchMaker.utils.ISO24751");
 
gpii.canopyMatchMaker.utils.ISO24751.ontologicalMetric = function (path, model) {
    var segs = fluid.pathUtil.parseEL(path);
    // if it's an application setting and the application is not matching, consider worst fit
    if (segs[0] === "applications" && fluid.get(model, ["applications", segs[1]]) === undefined) {
        return -Infinity;
    } else { // else do fitting as usual
        return gpii.canopyMatchMaker.utils.prefixLength(path, model);
    }
};
 
// only used by canopyMatchMaker - moved from MatchMakerFramework
gpii.canopyMatchMaker.utils.sortDescending = function (numA, numB) {
    return numB - numA;
};