| 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/>`)); |
| } |
|
|