File size: 4,609 Bytes
50f82a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
function analyzeScore(solution, endpointPath) {
    const modalElement = document.getElementById("scoreAnalysisModal");
    const modal = bootstrap.Modal.getOrCreateInstance(modalElement);
    modal.show();
    const scoreAnalysisModalContent = $("#scoreAnalysisModalContent");
    scoreAnalysisModalContent.children().remove();
    scoreAnalysisModalContent.text("");

    if (solution.score == null) {
        scoreAnalysisModalContent.text("Score not ready for analysis, try to run the solver first or wait until it advances.");
    } else {
        visualizeScoreAnalysis(scoreAnalysisModalContent, solution, endpointPath)
    }
}

function visualizeScoreAnalysis(scoreAnalysisModalContent, solution, endpointPath) {
    $('#scoreAnalysisScoreLabel').text(`(${solution.score})`);
    $.put(endpointPath, JSON.stringify(solution), function (scoreAnalysis) {
        let constraints = scoreAnalysis.constraints;
        constraints.sort(compareConstraintsBySeverity);
        constraints.map(addDerivedScoreAttributes);
        scoreAnalysis.constraints = constraints;

        const analysisTable = $(`<table class="table"/>`).css({textAlign: 'center'});
        const analysisTHead = $(`<thead/>`).append($(`<tr/>`)
            .append($(`<th></th>`))
            .append($(`<th>Constraint</th>`).css({textAlign: 'left'}))
            .append($(`<th>Type</th>`))
            .append($(`<th># Matches</th>`))
            .append($(`<th>Weight</th>`))
            .append($(`<th>Score</th>`))
            .append($(`<th></th>`)));
        analysisTable.append(analysisTHead);
        const analysisTBody = $(`<tbody/>`)
        $.each(scoreAnalysis.constraints, function (index, constraintAnalysis) {
            visualizeConstraintAnalysis(analysisTBody, index, constraintAnalysis)
        });
        analysisTable.append(analysisTBody);
        scoreAnalysisModalContent.append(analysisTable);
    }).fail(function (xhr, ajaxOptions, thrownError) {
            showError("Score analysis failed.", xhr);
        },
        "text");
}

function compareConstraintsBySeverity(a, b) {
    let aComponents = getScoreComponents(a.score), bComponents = getScoreComponents(b.score);
    if (aComponents.hard < 0 && bComponents.hard > 0) return -1;
    if (aComponents.hard > 0 && bComponents.soft < 0) return 1;
    if (Math.abs(aComponents.hard) > Math.abs(bComponents.hard)) {
        return -1;
    } else {
        if (aComponents.medium < 0 && bComponents.medium > 0) return -1;
        if (aComponents.medium > 0 && bComponents.medium < 0) return 1;
        if (Math.abs(aComponents.medium) > Math.abs(bComponents.medium)) {
            return -1;
        } else {
            if (aComponents.soft < 0 && bComponents.soft > 0) return -1;
            if (aComponents.soft > 0 && bComponents.soft < 0) return 1;

            return Math.abs(bComponents.soft) - Math.abs(aComponents.soft);
        }
    }
}

function addDerivedScoreAttributes(constraint) {
    let components = getScoreComponents(constraint.weight);
    constraint.type = components.hard != 0 ? 'hard' : (components.medium != 0 ? 'medium' : 'soft');
    constraint.weight = components[constraint.type];
    let scores = getScoreComponents(constraint.score);
    constraint.implicitScore = scores.hard != 0 ? scores.hard : (scores.medium != 0 ? scores.medium : scores.soft);
}

function getScoreComponents(score) {
    let components = {hard: 0, medium: 0, soft: 0};

    $.each([...score.matchAll(/(-?[0-9]+)(hard|medium|soft)/g)], function (i, parts) {
        components[parts[2]] = parseInt(parts[1], 10);
    });

    return components;
}

function visualizeConstraintAnalysis(analysisTBody, constraintIndex, constraintAnalysis, recommendation = false, recommendationIndex = null) {
    let icon = constraintAnalysis.type == "hard" && constraintAnalysis.implicitScore < 0 ? '<span class="fas fa-exclamation-triangle" style="color: red"></span>' : '';
    if (!icon) icon = constraintAnalysis.weight < 0 && constraintAnalysis.matches.length == 0 ? '<span class="fas fa-check-circle" style="color: green"></span>' : '';

    let row = $(`<tr/>`);
    row.append($(`<td/>`).html(icon))
        .append($(`<td/>`).text(constraintAnalysis.name).css({textAlign: 'left'}))
        .append($(`<td/>`).text(constraintAnalysis.type))
        .append($(`<td/>`).html(`<b>${constraintAnalysis.matches.length}</b>`))
        .append($(`<td/>`).text(constraintAnalysis.weight))
        .append($(`<td/>`).text(recommendation ? constraintAnalysis.score : constraintAnalysis.implicitScore));

    analysisTBody.append(row);
    row.append($(`<td/>`));
}