I'm trying to display data from Keen.io we're collecting from an iOS app where we report what devices of ours the app is used to communicate with. In particular I'm trying to find where there are more than one of our device.
An entry from the Keen collection looks like:
{
"iOS_OpenUDID": "hexadecimal",
"iOS_CountryCode": "two-letter country code",
"Device": {
"SerialNumber": "numbers",
"Barcode": "numbers",
"IP": "IP address",
"Model": "string",
},
"iOS_Timezone": "name of timezone",
}
Most of this is adapted from various different examples and tutorials, and as far as I can tell, it works. Examples I remember using are:
I have no idea though if it's 'good' code or 'awful', so please give constructive criticism:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Cache-Control" content="no-cache, mustrevalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<title>Multiple Devices</title>
<script type="text/javascript">
var Keen=Keen||{configure:function(e){this._cf=e},addEvent:function(e,t,n,i){this._eq=this._eq||[],this._eq.push([e,t,n,i])},setGlobalProperties:function(e){this._gp=e},onChartsReady:function(e){this._ocrq=this._ocrq||[],this._ocrq.push(e)}};(function(){var e=document.createElement("script");e.type="text/javascript",e.async=!0,e.src=("https:"==document.location.protocol?"https://":"http://")+"dc8na2hxrj29i.cloudfront.net/code/keen-2.1.0-min.js";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)})();
Keen.configure({
projectId: "REMOVED",
readKey: "REMOVED"
});
// From http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
function eliminateDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
function drawTable() {
var datatable = new google.visualization.DataTable();
datatable.addColumn('string', 'iOS OpenUDID');
datatable.addColumn('string', 'Models (Barcode, Serial Number, IP address)');
datatable.addColumn('string', 'App Brand');
datatable.addColumn('string', 'iOS Timezone');
var table = new google.visualization.Table(document.getElementById('table_div'));
var metric = new Keen.Metric("Devices", {
analysisType: "count_unique",
targetProperty: "Device.IP",
groupBy: "iOS_OpenUDID",
filters: [
{"property_name":"iOS_OpenUDID","operator":"exists","property_value":true},
]
});
metric.getResponse(function(response){
// console.log(response);
for (var id = 0; id < response.result.length; id++) {
if (response.result[id].result > 1) {
// console.log(response.result[id]);
var query = "https://api.keen.io/3.0/projects/REMOVED/queries/extraction?api_key=DEVICES&event_collection=Devices&timezone=43200&target_property=Device.Model&group_by=Device.Model&filters=%5B%7B%22property_name%22%3A%22iOS_OpenUDID%22%2C%22operator%22%3A%22eq%22%2C%22property_value%22%3A%22" + response.result[id].iOS_OpenUDID + "%22%7D%5D";
$.getJSON(query, function(data) {
// console.log(data);
var arr = [];
for (var i = 0; i < data.result.length; i++) {
if (data.result[i] !== undefined && data.result[i] !== null) {
var string = data.result[i].Device["Model"];
var string2 = "";
if (data.result[i].Device["Barcode"] !== undefined && data.result[i].Device["Barcode"] !== null) {
string2 += data.result[i].Device["Barcode"];
}
if (data.result[i].Device["SerialNumber"] !== undefined && data.result[i].Device["SerialNumber"] !== null) {
if (string2.length > 0) {
string2 += ", ";
}
string2 += data.result[i].Device["SerialNumber"];
}
if (data.result[i].Device["IP"] !== undefined && data.result[i].Device["IP"] !== null) {
if (string2.length > 0) {
string2 += ", ";
}
string2 += data.result[i].Device["IP"];
}
if (string2.length > 0) {
string += " (" + string2 + ")";
}
arr.push(string);
}
};
arr = eliminateDuplicates(arr);
var models = "";
for (var i = 0; i < arr.length; i++) {
if (models.length > 0) {
models += ", ";
}
models += arr[i];
}
datatable.addRow(
[data.result[0]["iOS_OpenUDID"], models, data.result[0]["Brand"], data.result[0]["iOS_Timezone"]]
);
table.draw(datatable, {showRowNumber: true});
})
.fail(function(jqXHR, textStatus, errorThrown) { alert('getJSON request failed! ' + textStatus); });
}
}
});
table.draw(datatable, {showRowNumber: true});
}
</script>
<style type="text/css">
body { padding-top: 70px; }
</style>
</head>
<body>
<div id='table_div'></div>
</body>
</html>
2 Answers 2
Nice work, @parsley72! This is a really clever mashup, no reason to suspect it's "not good" or even "awful" :)
[Keen IO employee here, fwiw]
One characteristic worth considering is reusability, but since this seems to be a fairly specialized bit of code I wouldn't spend too much time there. If you do find yourself repeating operations, there might be the beginnings of a reusable utility hiding in there somewhere. And if that's the case, please let me know.. it may be handy enough for us to roll into the library.
Entire eliminateDuplicates()
function can be now replaced simply with [...new Set(arr)]
.
Callback of $.getJSON
can be simplified. Instead of sea of if
s to filter out undefined
s and null
s and to join elements with a comma, it can be done this way:
const models = new Set();
for (const result of data.result) {
if (result === undefined || result === null) { continue; }
const arr = [result.Device.Barcode, result.Device.SerialNumber, result.Device.IP]
.filter(data => data !== undefined && data !== null).join(', ');
models.add(result.Device.Model + (arr.length > 0 ? ` (${arr})` : ''));
}
First all data get into an array, then undefined
s and null
s get filtered out, and all entries are joined with ,
. If there is anything in that array it get's appended to what will be added to models
. Notice also, that since models
is a Set
and not an array, it does not have to be checked for duplicates.
In your query, response.result[id].iOS_OpenUDID
is not encoded, while another part of it is (value of filters
param), worsening readability. Another approach:
const filters = encodeURIComponent(`[{"property_name":"iOS_OpenUDID","operator":"eq","property_value":"${that.iOS_OpenUDID}"}]`);
const query = '.........' + filters;
Rewrite
var Keen=Keen||{configure:function(e){this._cf=e},addEvent:function(e,t,n,i){this._eq=this._eq||[],this._eq.push([e,t,n,i])},setGlobalProperties:function(e){this._gp=e},onChartsReady:function(e){this._ocrq=this._ocrq||[],this._ocrq.push(e)}};(function(){var e=document.createElement('script');e.type='text/javascript',e.async=!0,e.src=('https:'==document.location.protocol?'https://':'http://')+'dc8na2hxrj29i.cloudfront.net/code/keen-2.1.0-min.js';var t=document.getElementsByTagName('script')[0];t.parentNode.insertBefore(e,t)})();
Keen.configure({
projectId: 'REMOVED',
readKey: 'REMOVED'
});
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
const datatable = new google.visualization.DataTable();
datatable.addColumn('string', 'iOS OpenUDID');
datatable.addColumn('string', 'Models (Barcode, Serial Number, IP address)');
datatable.addColumn('string', 'App Brand');
datatable.addColumn('string', 'iOS Timezone');
const table = new google.visualization.Table(document.getElementById('table_div'));
const onFetch = data => {
// console.log(data);
const models = new Set();
for (const result of data.result) {
if (result === undefined || result === null) { continue; }
const arr = [result.Device.Barcode, result.Device.SerialNumber, result.Device.IP]
.filter(data => data !== undefined && data !== null).join(', ');
models.add(result.Device.Model + (arr.length > 0 ? ` (${arr})` : ''));
}
datatable.addRow([
data.result[0].iOS_OpenUDID,
[...models].join(', '),
data.result[0].Brand,
data.result[0].iOS_Timezone
]);
table.draw(datatable, {showRowNumber: true});
};
function drawTable() {
const metric = new Keen.Metric('Devices', {
analysisType: 'count_unique',
targetProperty: 'Device.IP',
groupBy: 'iOS_OpenUDID',
filters: [{'property_name': 'iOS_OpenUDID', 'operator': 'exists', 'property_value': true}]
});
metric.getResponse(function(response) {
// console.log(response);
for (const that of response.result) {
if (that.result <= 1) { continue; }
// console.log(that);
const filters = encodeURIComponent(`[{"property_name":"iOS_OpenUDID","operator":"eq","property_value":"${that.iOS_OpenUDID}"}]`);
const query = 'https://api.keen.io/3.0/projects/REMOVED/queries/extraction?api_key=DEVICES&event_collection=Devices&timezone=43200&target_property=Device.Model&group_by=Device.Model&filters=' + filters;
fetch(query).then(response => response.json()).then(onFetch).catch(reason =>
alert(`fetch, JSONing or onFetch function failed. Reason: ${reason}`)
);
}
});
table.draw(datatable, {showRowNumber: true});
}
-
\$\begingroup\$ Ahoy! Do you ever intend to un-delete this answer? \$\endgroup\$2020年10月28日 18:57:44 +00:00Commented Oct 28, 2020 at 18:57
-
\$\begingroup\$ @SᴀᴍOnᴇᴌᴀ: done \$\endgroup\$Przemek– Przemek2021年02月17日 21:35:15 +00:00Commented Feb 17, 2021 at 21:35