2010-10-20 20:11:15 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright 2010, Google Inc.
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are
|
|
|
|
met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
|
|
copyright notice, this list of conditions and the following disclaimer
|
|
|
|
in the documentation and/or other materials provided with the
|
|
|
|
distribution.
|
|
|
|
* Neither the name of Google Inc. nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived from
|
|
|
|
this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
2016-11-15 08:34:19 +01:00
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
2010-10-20 20:11:15 +02:00
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
function ClusteringDialog(columnName, expression) {
|
2010-03-02 20:58:47 +01:00
|
|
|
this._columnName = columnName;
|
2010-03-02 21:33:11 +01:00
|
|
|
this._expression = expression;
|
2010-03-06 11:17:58 +01:00
|
|
|
this._method = "binning";
|
|
|
|
this._function = "fingerprint";
|
|
|
|
this._params = {};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._facets = [];
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-02 20:58:47 +01:00
|
|
|
this._createDialog();
|
|
|
|
this._cluster();
|
|
|
|
}
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._createDialog = function() {
|
2010-03-02 20:58:47 +01:00
|
|
|
var self = this;
|
2010-06-21 23:59:53 +02:00
|
|
|
var dialog = $(DOM.loadHTML("core", "scripts/dialogs/clustering-dialog.html"));
|
|
|
|
|
|
|
|
this._elmts = DOM.bind(dialog);
|
2018-11-21 18:30:00 +01:00
|
|
|
this._elmts.dialogHeader.text($.i18n('core-dialogs/cluster-edit')+' "' + this._columnName + '"');
|
|
|
|
|
|
|
|
this._elmts.or_dialog_descr.html($.i18n('core-dialogs/cluster-descr'));
|
|
|
|
this._elmts.or_dialog_findMore.html($.i18n('core-dialogs/find-more'));
|
|
|
|
this._elmts.or_dialog_method.html($.i18n('core-dialogs/method'));
|
|
|
|
this._elmts.or_dialog_keyCollision.html($.i18n('core-dialogs/key-collision'));
|
|
|
|
this._elmts.or_dialog_neighbor.html($.i18n('core-dialogs/nearest-neighbor'));
|
|
|
|
this._elmts.or_dialog_keying.html($.i18n('core-dialogs/keying-function'));
|
|
|
|
this._elmts.or_dialog_ngramSize.html($.i18n('core-dialogs/ngram-size'));
|
|
|
|
this._elmts.or_dialog_radius.html($.i18n('core-dialogs/ngram-radius'));
|
|
|
|
this._elmts.or_dialog_blockChars.html($.i18n('core-dialogs/block-chars'));
|
|
|
|
this._elmts.selectAllButton.html($.i18n('core-buttons/select-all'));
|
|
|
|
this._elmts.deselectAllButton.html($.i18n('core-buttons/unselect-all'));
|
|
|
|
this._elmts.exportClusterButton.html($.i18n('core-buttons/export-cluster'));
|
|
|
|
this._elmts.applyReClusterButton.html($.i18n('core-buttons/merge-cluster'));
|
|
|
|
this._elmts.applyCloseButton.html($.i18n('core-buttons/merge-close'));
|
|
|
|
this._elmts.closeButton.html($.i18n('core-buttons/close'));
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-06 11:17:58 +01:00
|
|
|
this._elmts.methodSelector.change(function() {
|
|
|
|
var selection = $(this).find("option:selected").text();
|
2018-11-21 18:30:00 +01:00
|
|
|
if (selection == $.i18n('core-dialogs/key-collision')) {
|
2010-06-30 19:29:03 +02:00
|
|
|
dialog.find(".binning-controls").show();
|
|
|
|
dialog.find(".knn-controls").hide();
|
2010-03-06 11:17:58 +01:00
|
|
|
self._method = "binning";
|
|
|
|
self._elmts.keyingFunctionSelector.change();
|
2018-11-21 18:30:00 +01:00
|
|
|
} else if (selection === $.i18n('core-dialogs/nearest-neighbor')) {
|
2010-06-30 19:29:03 +02:00
|
|
|
dialog.find(".binning-controls").hide();
|
|
|
|
dialog.find(".knn-controls").show();
|
2010-03-06 11:17:58 +01:00
|
|
|
self._method = "knn";
|
|
|
|
self._elmts.distanceFunctionSelector.change();
|
|
|
|
}
|
|
|
|
});
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-06 11:17:58 +01:00
|
|
|
var changer = function() {
|
2016-11-15 08:34:19 +01:00
|
|
|
self._function = $(this).find("option:selected").val();
|
2010-03-06 11:17:58 +01:00
|
|
|
$(".function-params").hide();
|
|
|
|
$("#" + self._function + "-params").show();
|
2010-03-10 02:18:41 +01:00
|
|
|
params_changer();
|
2010-03-06 11:17:58 +01:00
|
|
|
};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-06 11:17:58 +01:00
|
|
|
this._elmts.keyingFunctionSelector.change(changer);
|
|
|
|
this._elmts.distanceFunctionSelector.change(changer);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-10 02:18:41 +01:00
|
|
|
var params_changer = function() {
|
|
|
|
self._params = {};
|
|
|
|
$(".dialog-body input.param:visible").each(function() {
|
|
|
|
var e = $(this);
|
|
|
|
var name = e.attr('name');
|
|
|
|
var datatype = e.attr('datatype') || 'string';
|
2016-11-15 08:34:19 +01:00
|
|
|
var value = e.val();
|
2010-03-10 02:18:41 +01:00
|
|
|
if (datatype == 'int') {
|
2010-04-08 20:21:28 +02:00
|
|
|
value = parseInt(value,10);
|
2010-03-10 02:18:41 +01:00
|
|
|
} else if (datatype == 'float') {
|
|
|
|
value = parseFloat(value);
|
2016-11-15 08:34:19 +01:00
|
|
|
}
|
2010-03-10 02:18:41 +01:00
|
|
|
self._params[name] = value;
|
|
|
|
});
|
|
|
|
self._cluster();
|
|
|
|
};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-10 02:18:41 +01:00
|
|
|
this._elmts.ngramSize.change(params_changer);
|
|
|
|
this._elmts.radius.change(params_changer);
|
|
|
|
this._elmts.ngramBlock.change(params_changer);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-06-21 23:59:53 +02:00
|
|
|
this._elmts.selectAllButton.click(function() { self._selectAll(); });
|
|
|
|
this._elmts.deselectAllButton.click(function() { self._deselectAll(); });
|
2015-11-18 20:17:29 +01:00
|
|
|
this._elmts.exportClusterButton.click(function() { self._onExportCluster(); });
|
2010-06-21 23:59:53 +02:00
|
|
|
this._elmts.applyReClusterButton.click(function() { self._onApplyReCluster(); });
|
|
|
|
this._elmts.applyCloseButton.click(function() { self._onApplyClose(); });
|
|
|
|
this._elmts.closeButton.click(function() { self._dismiss(); });
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2018-12-11 12:59:37 +01:00
|
|
|
// Fill in all the keyers and distances
|
|
|
|
$.get("command/core/get-clustering-functions-and-distances")
|
|
|
|
.success(function(data) {
|
|
|
|
var keyers = data.keyers != null ? data.keyers : [];
|
|
|
|
var distances = data.distances != null ? data.distances : [];
|
|
|
|
var i = 0;
|
|
|
|
for(; i < keyers.length; i++) {
|
2018-12-11 13:16:58 +01:00
|
|
|
var label = $.i18n('clustering-keyers/'+keyers[i]);
|
|
|
|
if (label.startsWith('clustering-keyers')) {
|
|
|
|
label = keyers[i];
|
|
|
|
}
|
2018-12-11 12:59:37 +01:00
|
|
|
var option = $('<option></option>')
|
|
|
|
.attr('value', keyers[i])
|
2018-12-11 13:16:58 +01:00
|
|
|
.text(label)
|
2018-12-11 12:59:37 +01:00
|
|
|
.appendTo(self._elmts.keyingFunctionSelector);
|
|
|
|
if (i == 0) {
|
|
|
|
option.attr('selected', 'true');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(i = 0; i < distances.length; i++) {
|
2018-12-11 13:16:58 +01:00
|
|
|
var label = $.i18n('clustering-distances/'+distances[i]);
|
|
|
|
if (label.startsWith('clustering-distances')) {
|
|
|
|
label = distances[i];
|
|
|
|
}
|
2018-12-11 12:59:37 +01:00
|
|
|
var option = $('<option></option>')
|
|
|
|
.attr('value', distances[i])
|
2018-12-11 13:16:58 +01:00
|
|
|
.text(label)
|
2018-12-11 12:59:37 +01:00
|
|
|
.appendTo(self._elmts.distanceFunctionSelector);
|
|
|
|
if (i == 0) {
|
|
|
|
option.attr('selected', 'true');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self._level = DialogSystem.showDialog(dialog);
|
|
|
|
})
|
|
|
|
.error(function(error) {
|
|
|
|
alert($.i18n('core-dialogs/no-clustering-functions-and-distances'));
|
|
|
|
});
|
2010-03-02 20:58:47 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._renderTable = function(clusters) {
|
2010-03-02 20:58:47 +01:00
|
|
|
var self = this;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-07 00:01:46 +01:00
|
|
|
var container = this._elmts.tableContainer;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
if (clusters.length > 0) {
|
|
|
|
var table = $('<table></table>').addClass("clustering-dialog-entry-table")[0];
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
var trHead = table.insertRow(table.rows.length);
|
|
|
|
trHead.className = "header";
|
2018-11-21 18:30:00 +01:00
|
|
|
$(trHead.insertCell(0)).text($.i18n('core-dialogs/cluster-size'));
|
|
|
|
$(trHead.insertCell(1)).text($.i18n('core-dialogs/row-count'));
|
|
|
|
$(trHead.insertCell(2)).text($.i18n('core-dialogs/cluster-values'));
|
|
|
|
$(trHead.insertCell(3)).text($.i18n('core-dialogs/merge'));
|
|
|
|
$(trHead.insertCell(4)).text($.i18n('core-dialogs/new-cell-val'));
|
2010-03-13 10:32:06 +01:00
|
|
|
|
|
|
|
var renderCluster = function(cluster) {
|
|
|
|
var tr = table.insertRow(table.rows.length);
|
2010-04-08 20:21:28 +02:00
|
|
|
tr.className = table.rows.length % 2 === 0 ? "odd" : "even";
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
$(tr.insertCell(0)).text(cluster.choices.length);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
$(tr.insertCell(1)).text(cluster.rowCount);
|
2010-03-16 07:53:08 +01:00
|
|
|
|
|
|
|
var facet = {
|
|
|
|
"c": {
|
|
|
|
"type":"list",
|
|
|
|
"name": self._columnName,
|
|
|
|
"columnName": self._columnName,
|
|
|
|
"expression":"value"
|
|
|
|
},
|
|
|
|
"o":{
|
|
|
|
"sort":"name"
|
|
|
|
},
|
|
|
|
"s":[
|
|
|
|
]
|
|
|
|
};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
var ul = $('<ul></ul>');
|
|
|
|
var choices = cluster.choices;
|
|
|
|
var rowCount = 0;
|
2010-04-08 20:21:28 +02:00
|
|
|
var onClick = function() {
|
|
|
|
var parent = $(this).closest("tr");
|
2010-09-30 19:18:19 +02:00
|
|
|
var value = $(this).text();
|
|
|
|
cluster.value = value;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-09-30 19:18:19 +02:00
|
|
|
parent.find("input[type='text']").val(value);
|
2015-09-11 22:28:15 +02:00
|
|
|
var checkbox = parent.find("input[type='checkbox']");
|
2015-10-17 00:53:21 +02:00
|
|
|
checkbox.prop('checked', true).change();
|
2010-04-08 20:21:28 +02:00
|
|
|
return false;
|
|
|
|
};
|
2010-03-13 10:32:06 +01:00
|
|
|
for (var c = 0; c < choices.length; c++) {
|
|
|
|
var choice = choices[c];
|
|
|
|
var li = $('<li></li>');
|
2018-11-21 18:30:00 +01:00
|
|
|
$('<a href="javascript:{}" title='+$.i18n('core-dialogs/use-this-val')+'></a>').text(choice.v).click(onClick).appendTo(li);
|
2010-03-13 10:32:06 +01:00
|
|
|
$('<span></span>').text("(" + choice.c + " rows)").addClass("clustering-dialog-entry-count").appendTo(li);
|
|
|
|
rowCount += choice.c;
|
2010-03-16 07:53:08 +01:00
|
|
|
facet.s[c] = {
|
|
|
|
"v": {
|
|
|
|
"v":choice.v,
|
|
|
|
"l":choice.v
|
|
|
|
}
|
|
|
|
};
|
2010-03-13 10:32:06 +01:00
|
|
|
li.appendTo(ul);
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-16 07:53:08 +01:00
|
|
|
var params = [
|
2015-10-16 21:21:21 +02:00
|
|
|
"project=" + encodeURIComponent(theProject.id),
|
|
|
|
"ui=" + encodeURIComponent(JSON.stringify({
|
2010-03-16 07:53:08 +01:00
|
|
|
"facets" : [ facet ]
|
|
|
|
}))
|
|
|
|
];
|
2010-07-01 09:47:45 +02:00
|
|
|
var url = "project?" + params.join("&");
|
2015-10-16 21:21:21 +02:00
|
|
|
|
2010-03-16 07:53:08 +01:00
|
|
|
var div = $('<div></div>').addClass("clustering-dialog-value-focus");
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2018-11-21 18:30:00 +01:00
|
|
|
var browseLink = $('<a target="_new" title="'+$.i18n('core-dialogs/browse-only-these')+'">'+$.i18n('core-dialogs/browse-this-cluster')+'</a>')
|
2010-03-16 07:53:08 +01:00
|
|
|
.addClass("clustering-dialog-browse-focus")
|
|
|
|
.attr("href",url)
|
|
|
|
.css("visibility","hidden")
|
|
|
|
.appendTo(div);
|
|
|
|
|
|
|
|
$(tr.insertCell(2))
|
|
|
|
.mouseenter(function() { browseLink.css("visibility", "visible"); })
|
|
|
|
.mouseleave(function() { browseLink.css("visibility", "hidden"); })
|
|
|
|
.append(ul)
|
|
|
|
.append(div);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
var editCheck = $('<input type="checkbox" />')
|
|
|
|
.change(function() {
|
2015-10-17 00:53:21 +02:00
|
|
|
cluster.edit = this.checked;
|
2010-03-13 10:32:06 +01:00
|
|
|
}).appendTo(tr.insertCell(3));
|
|
|
|
|
|
|
|
if (cluster.edit) {
|
|
|
|
editCheck.attr("checked", "true");
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2013-04-13 23:04:03 +02:00
|
|
|
var input = $('<input type="text" size="25" />')
|
2010-03-13 10:32:06 +01:00
|
|
|
.attr("value", cluster.value)
|
2013-06-23 19:37:29 +02:00
|
|
|
.bind("keyup change input",function() {
|
2010-03-13 10:32:06 +01:00
|
|
|
cluster.value = this.value;
|
|
|
|
}).appendTo(tr.insertCell(4));
|
|
|
|
};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
for (var i = 0; i < clusters.length; i++) {
|
|
|
|
renderCluster(clusters[i]);
|
2010-03-02 20:58:47 +01:00
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
container.empty().append(table);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
this._elmts.resultSummary.html(
|
|
|
|
(clusters.length === this._clusters.length) ?
|
2020-06-14 10:23:23 +02:00
|
|
|
$.i18n('core-dialogs/clusters-found', this._clusters.length) :
|
|
|
|
$.i18n('core-dialogs/clusters-filtered', clusters.length, this._clusters.length)
|
2010-03-13 10:32:06 +01:00
|
|
|
);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
} else {
|
|
|
|
container.html(
|
2018-11-21 18:30:00 +01:00
|
|
|
'<div style="margin: 2em;"><div style="font-size: 130%; color: #333;">'+$.i18n('core-dialogs/no-cluster-found')+'</div><div style="padding-top: 1em; font-size: 110%; color: #888;">'+$.i18n('core-dialogs/try-another-method')+'</div></div>'
|
2010-03-13 10:32:06 +01:00
|
|
|
);
|
2010-03-02 20:58:47 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._cluster = function() {
|
2020-06-12 19:08:41 +02:00
|
|
|
$(".clustering-dialog-facet").css("display","none");
|
2010-03-06 11:17:58 +01:00
|
|
|
var self = this;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-06 11:17:58 +01:00
|
|
|
var container = this._elmts.tableContainer.html(
|
2018-11-21 18:30:00 +01:00
|
|
|
'<div style="margin: 1em; font-size: 130%; color: #888;">'+$.i18n('core-dialogs/clustering')+'<img src="images/small-spinner.gif"></div>'
|
2010-03-06 11:17:58 +01:00
|
|
|
);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-07 00:01:46 +01:00
|
|
|
this._elmts.resultSummary.empty();
|
2010-03-02 20:58:47 +01:00
|
|
|
|
2010-03-06 11:17:58 +01:00
|
|
|
$.post(
|
2012-10-13 19:47:08 +02:00
|
|
|
"command/core/compute-clusters?" + $.param({ project: theProject.id }),
|
2016-11-15 08:34:19 +01:00
|
|
|
{
|
|
|
|
engine: JSON.stringify(ui.browsingEngine.getJSON()),
|
|
|
|
clusterer: JSON.stringify({
|
|
|
|
'type' : this._method,
|
2010-03-06 11:17:58 +01:00
|
|
|
'function' : this._function,
|
|
|
|
'column' : this._columnName,
|
|
|
|
'params' : this._params
|
2016-11-15 08:34:19 +01:00
|
|
|
})
|
2010-03-06 11:17:58 +01:00
|
|
|
},
|
|
|
|
function(data) {
|
2010-03-09 00:05:44 +01:00
|
|
|
self._updateData(data);
|
2020-06-12 19:08:41 +02:00
|
|
|
$(".clustering-dialog-facet").css("display","block");
|
2010-03-06 11:17:58 +01:00
|
|
|
},
|
|
|
|
"json"
|
|
|
|
);
|
2010-04-08 20:21:28 +02:00
|
|
|
};
|
2010-03-02 21:33:11 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._updateData = function(data) {
|
2010-03-09 00:05:44 +01:00
|
|
|
var clusters = [];
|
|
|
|
$.each(data, function() {
|
|
|
|
var cluster = {
|
2010-03-09 21:58:51 +01:00
|
|
|
edit: false,
|
2010-03-09 00:05:44 +01:00
|
|
|
choices: this,
|
|
|
|
value: this[0].v,
|
|
|
|
size: this.length
|
|
|
|
};
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
var sum = 0;
|
|
|
|
var sumSquared = 0;
|
|
|
|
var rowCount = 0;
|
|
|
|
$.each(cluster.choices, function() {
|
|
|
|
rowCount += this.c;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
|
|
|
var l = this.v.length;
|
2010-03-09 00:05:44 +01:00
|
|
|
sum += l;
|
|
|
|
sumSquared += l * l;
|
|
|
|
});
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
cluster.rowCount = rowCount;
|
|
|
|
cluster.avg = sum / cluster.choices.length;
|
|
|
|
cluster.variance = Math.sqrt(sumSquared / cluster.choices.length - cluster.avg * cluster.avg);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
clusters.push(cluster);
|
|
|
|
});
|
|
|
|
this._clusters = clusters;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._resetFacets();
|
|
|
|
this._updateAll();
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._selectAll = function() {
|
|
|
|
$(".clustering-dialog-entry-table input:not(:checked)").attr('checked', true).change();
|
2010-03-09 21:58:51 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._deselectAll = function() {
|
|
|
|
$(".clustering-dialog-entry-table input:checked").attr('checked', false).change();
|
2010-03-09 21:58:51 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._onApplyClose = function() {
|
2016-11-15 08:34:19 +01:00
|
|
|
var self = this;
|
2010-03-07 00:34:27 +01:00
|
|
|
this._apply(function() {
|
|
|
|
self._dismiss();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._onApplyReCluster = function() {
|
2016-11-15 08:34:19 +01:00
|
|
|
var self = this;
|
2010-03-07 00:34:27 +01:00
|
|
|
this._apply(function() {
|
|
|
|
self._cluster();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-11-18 20:17:29 +01:00
|
|
|
ClusteringDialog.prototype._onExportCluster = function() {
|
2017-02-10 15:22:31 +01:00
|
|
|
var self = this;
|
2015-11-18 20:17:29 +01:00
|
|
|
self._export();
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._apply = function(onDone) {
|
2010-03-09 00:05:44 +01:00
|
|
|
var clusters = this._getRestrictedClusters();
|
2010-03-02 21:33:11 +01:00
|
|
|
var edits = [];
|
2010-03-09 00:05:44 +01:00
|
|
|
for (var i = 0; i < clusters.length; i++) {
|
|
|
|
var cluster = clusters[i];
|
2010-03-02 21:33:11 +01:00
|
|
|
if (cluster.edit) {
|
|
|
|
var values = [];
|
|
|
|
for (var j = 0; j < cluster.choices.length; j++) {
|
2010-03-06 11:17:58 +01:00
|
|
|
values.push(cluster.choices[j].v);
|
2010-03-02 21:33:11 +01:00
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-02 21:33:11 +01:00
|
|
|
edits.push({
|
|
|
|
from: values,
|
|
|
|
to: cluster.value
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-02 21:33:11 +01:00
|
|
|
if (edits.length > 0) {
|
2010-09-23 00:55:28 +02:00
|
|
|
Refine.postCoreProcess(
|
2010-03-07 01:25:00 +01:00
|
|
|
"mass-edit",
|
2010-03-02 21:33:11 +01:00
|
|
|
{},
|
|
|
|
{
|
|
|
|
columnName: this._columnName,
|
|
|
|
expression: this._expression,
|
|
|
|
edits: JSON.stringify(edits)
|
|
|
|
},
|
2010-03-07 00:34:27 +01:00
|
|
|
{ cellsChanged: true },
|
|
|
|
{
|
|
|
|
onError: function(o) {
|
|
|
|
alert("Error: " + o.message);
|
|
|
|
},
|
|
|
|
onDone: onDone
|
|
|
|
}
|
2010-03-02 21:33:11 +01:00
|
|
|
);
|
|
|
|
} else {
|
2018-11-21 18:30:00 +01:00
|
|
|
alert($.i18n('core-dialogs/warning-check-boxes'));
|
2010-03-02 21:33:11 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-18 20:17:29 +01:00
|
|
|
ClusteringDialog.prototype._export = function() {
|
|
|
|
var clusters = this._getRestrictedClusters();
|
2015-11-18 20:25:13 +01:00
|
|
|
var projectName = theProject.metadata.name;
|
|
|
|
var columnName = this._columnName;
|
2015-11-18 20:26:24 +01:00
|
|
|
var timeStamp = (new Date()).toISOString();
|
2015-11-18 20:25:13 +01:00
|
|
|
var obj = {
|
|
|
|
'projectName': projectName,
|
|
|
|
'columnName': columnName,
|
2015-11-18 20:26:24 +01:00
|
|
|
'timeStamp': timeStamp,
|
2015-11-18 20:27:33 +01:00
|
|
|
'clusterMethod': this._method,
|
|
|
|
'keyingFunction': this._function,
|
2017-02-10 15:22:31 +01:00
|
|
|
'clusters': clusters
|
2015-11-18 20:25:13 +01:00
|
|
|
};
|
|
|
|
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj));
|
2017-02-10 15:22:31 +01:00
|
|
|
var link = document.createElement('a');
|
|
|
|
link.href = 'data:' + data;
|
|
|
|
link.download = "clusters_" + projectName + "_" + columnName + "_" + timeStamp + ".json";
|
2017-02-10 18:18:07 +01:00
|
|
|
document.body.appendChild(link);
|
2015-11-18 20:17:29 +01:00
|
|
|
link.click();
|
2017-02-10 18:18:07 +01:00
|
|
|
document.body.removeChild(link);
|
2015-11-18 20:17:29 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._dismiss = function() {
|
2010-03-02 21:33:11 +01:00
|
|
|
DialogSystem.dismissUntil(this._level - 1);
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._getBaseClusters = function() {
|
2010-03-09 00:05:44 +01:00
|
|
|
return [].concat(this._clusters);
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._getRestrictedClusters = function(except) {
|
2010-03-09 00:05:44 +01:00
|
|
|
var clusters = this._getBaseClusters();
|
|
|
|
for (var i = 0; i < this._facets.length; i++) {
|
|
|
|
var facet = this._facets[i].facet;
|
|
|
|
if (except !== facet) {
|
|
|
|
clusters = facet.restrict(clusters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return clusters;
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._updateAll = function() {
|
2010-03-09 00:05:44 +01:00
|
|
|
for (var i = 0; i < this._facets.length; i++) {
|
|
|
|
var facet = this._facets[i].facet;
|
|
|
|
var clusters = this._getRestrictedClusters(facet);
|
|
|
|
facet.update(clusters);
|
|
|
|
}
|
|
|
|
this._renderTable(this._getRestrictedClusters());
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._resetFacets = function() {
|
2010-03-09 00:05:44 +01:00
|
|
|
for (var i = 0; i < this._facets.length; i++) {
|
|
|
|
var r = this._facets[i];
|
|
|
|
r.facet.dispose();
|
|
|
|
r.elmt.remove();
|
|
|
|
}
|
|
|
|
this._facets = [];
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2018-11-21 18:30:00 +01:00
|
|
|
this._createFacet($.i18n('core-dialogs/choices-in-cluster'), "size");
|
|
|
|
this._createFacet($.i18n('core-dialogs/rows-in-cluster'), "rowCount");
|
|
|
|
this._createFacet($.i18n('core-dialogs/choice-avg-length'), "avg");
|
|
|
|
this._createFacet($.i18n('core-dialogs/choice-var-length'), "variance");
|
2010-03-09 00:05:44 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.prototype._createFacet = function(title, property) {
|
2010-03-09 00:05:44 +01:00
|
|
|
var elmt = $('<div>').appendTo(this._elmts.facetContainer);
|
|
|
|
this._facets.push({
|
|
|
|
elmt: elmt,
|
2010-03-13 10:32:06 +01:00
|
|
|
facet: new ClusteringDialog.Facet(this, title, property, elmt, this._getBaseClusters())
|
2010-03-09 00:05:44 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet = function(dialog, title, property, elmt, clusters) {
|
2010-03-09 00:05:44 +01:00
|
|
|
this._dialog = dialog;
|
|
|
|
this._property = property;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
var self = this;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
var max = Number.NEGATIVE_INFINITY;
|
|
|
|
var min = Number.POSITIVE_INFINITY;
|
|
|
|
for (var i = 0; i < clusters.length; i++) {
|
|
|
|
var cluster = clusters[i];
|
|
|
|
var val = cluster[property];
|
|
|
|
max = Math.max(max, val);
|
|
|
|
min = Math.min(min, val);
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._min = min;
|
|
|
|
this._max = max;
|
|
|
|
if (min >= max) {
|
|
|
|
this._step = 0;
|
2010-03-09 01:41:33 +01:00
|
|
|
this._baseBins = [];
|
2010-03-09 00:05:44 +01:00
|
|
|
} else {
|
|
|
|
var diff = max - min;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._step = 1;
|
|
|
|
if (diff > 10) {
|
|
|
|
while (this._step * 100 < diff) {
|
|
|
|
this._step *= 10;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (this._step * 100 > diff) {
|
|
|
|
this._step /= 10;
|
|
|
|
}
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._min = (Math.floor(this._min / this._step) * this._step);
|
|
|
|
this._max = (Math.ceil(this._max / this._step) * this._step);
|
|
|
|
this._binCount = 1 + Math.ceil((this._max - this._min) / this._step);
|
|
|
|
if (this._binCount > 100) {
|
|
|
|
this._step *= 2;
|
|
|
|
this._binCount = Math.round((1 + this._binCount) / 2);
|
2010-03-18 23:58:06 +01:00
|
|
|
} else if (this._binCount < 3) {
|
|
|
|
this._step /= 2;
|
|
|
|
this._binCount *= 2;
|
|
|
|
this._max = (Math.ceil(this._max / this._step) * this._step);
|
2010-03-09 00:05:44 +01:00
|
|
|
}
|
|
|
|
this._baseBins = this._computeDistribution(clusters);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._from = this._min;
|
|
|
|
this._to = this._max;
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
elmt.addClass("clustering-dialog-facet");
|
2010-03-09 00:05:44 +01:00
|
|
|
var html = $(
|
2010-03-13 10:32:06 +01:00
|
|
|
'<div class="clustering-dialog-facet-header">' + title + '</div>' +
|
2010-04-20 01:27:57 +02:00
|
|
|
'<div class="clustering-dialog-facet-slider" bind="sliderWidgetDiv">' +
|
|
|
|
'<div class="clustering-dialog-facet-histogram" bind="histogramContainer"></div>' +
|
|
|
|
'</div>' +
|
2010-03-13 10:32:06 +01:00
|
|
|
'<div class="clustering-dialog-facet-selection" bind="selectionContainer"></div>'
|
2010-03-09 00:05:44 +01:00
|
|
|
).appendTo(elmt);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._elmts = DOM.bind(html);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-18 23:58:06 +01:00
|
|
|
this._histogram = new HistogramWidget(this._elmts.histogramContainer, { binColors: [ "#ccccff", "#6666ff" ] });
|
2010-04-20 01:27:57 +02:00
|
|
|
this._sliderWidget = new SliderWidget(this._elmts.sliderWidgetDiv);
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-04-20 01:27:57 +02:00
|
|
|
this._elmts.sliderWidgetDiv.bind("slide", function(evt, data) {
|
|
|
|
self._from = data.from;
|
|
|
|
self._to = data.to;
|
|
|
|
self._setRangeIndicators();
|
|
|
|
}).bind("stop", function(evt, data) {
|
|
|
|
self._from = data.from;
|
|
|
|
self._to = data.to;
|
|
|
|
self._setRangeIndicators();
|
|
|
|
self._dialog._updateAll();
|
2010-03-09 00:05:44 +01:00
|
|
|
});
|
2010-04-20 01:27:57 +02:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
this._setRangeIndicators();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet.prototype.dispose = function() {
|
2010-03-09 00:05:44 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet.prototype.restrict = function(clusters) {
|
2010-04-08 20:21:28 +02:00
|
|
|
if (!this._baseBins.length || (this._from == this._min && this._to == this._max)) {
|
2010-03-09 00:05:44 +01:00
|
|
|
return clusters;
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
var clusters2 = [];
|
|
|
|
for (var i = 0; i < clusters.length; i++) {
|
|
|
|
var cluster = clusters[i];
|
|
|
|
var val = cluster[this._property];
|
|
|
|
if (val >= this._from && val <= this._to) {
|
|
|
|
clusters2.push(cluster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return clusters2;
|
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet.prototype.update = function(clusters) {
|
2010-04-08 20:21:28 +02:00
|
|
|
if (!this._baseBins.length) {
|
2010-03-09 00:05:44 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
var bins = this._computeDistribution(clusters);
|
2010-03-18 21:45:52 +01:00
|
|
|
|
2010-04-20 01:27:57 +02:00
|
|
|
this._sliderWidget.update(
|
2016-11-15 08:34:19 +01:00
|
|
|
this._min,
|
|
|
|
this._max,
|
|
|
|
this._step,
|
2010-03-18 23:58:06 +01:00
|
|
|
this._from,
|
|
|
|
this._to
|
2010-03-18 21:45:52 +01:00
|
|
|
);
|
2010-04-20 01:27:57 +02:00
|
|
|
this._histogram.update(
|
2016-11-15 08:34:19 +01:00
|
|
|
this._min,
|
|
|
|
this._max,
|
|
|
|
this._step,
|
2010-04-20 01:27:57 +02:00
|
|
|
[ this._baseBins, bins ]
|
|
|
|
);
|
2010-03-09 00:05:44 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet.prototype._setRangeIndicators = function() {
|
2010-04-20 01:27:57 +02:00
|
|
|
this._elmts.selectionContainer.html(this._from + " — " + this._to);
|
2010-03-09 00:05:44 +01:00
|
|
|
};
|
|
|
|
|
2010-03-13 10:32:06 +01:00
|
|
|
ClusteringDialog.Facet.prototype._computeDistribution = function(clusters) {
|
2010-03-09 00:05:44 +01:00
|
|
|
var bins = [];
|
|
|
|
for (var b = 0; b < this._binCount; b++) {
|
|
|
|
bins.push(0);
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
for (var i = 0; i < clusters.length; i++) {
|
|
|
|
var cluster = clusters[i];
|
|
|
|
var val = cluster[this._property];
|
|
|
|
var bin = Math.round((val - this._min) / this._step);
|
|
|
|
bins[bin]++;
|
|
|
|
}
|
2016-11-15 08:34:19 +01:00
|
|
|
|
2010-03-09 00:05:44 +01:00
|
|
|
return bins;
|
|
|
|
};
|