kanaui 5.0.2 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/kanaui/kiddo/kiddo_initialize.js +222 -39
- data/lib/kanaui/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42c66ccd44ece7ec950473fb78babede09820bc172266b706dae65bc8c6a918a
|
|
4
|
+
data.tar.gz: 479b475c455f7206e5b6dc9cee937d2233024fb6315ba45707f849026ed492b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 349c9dfbd70cef4a3a99416dfba1320850cb9e006d062f5bb2b6002b1dfa03b3b9b20c91e5473a292f57138ad69c5792793ffa699d5023442409defdd6bb1624
|
|
7
|
+
data.tar.gz: edaeac825ea58a5a349853cbe1b5a74a583cc147f75118d14b99a31cc118d547c51e9fc863e22d710eea6b00c6b95d515ae24cb89cd266fffe14625b2c8346f3
|
|
@@ -21,57 +21,240 @@
|
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Workaround for a MariaDB JDBC connector v3.x parser bug in the bundled
|
|
25
|
+
// analytics plugin: any request that contains a `^dimension:` clause causes
|
|
26
|
+
// the plugin to generate a `CASE ... END AS \`field\`` expression that the
|
|
27
|
+
// driver mangles, returning a 500 ("near '' at line N").
|
|
28
|
+
//
|
|
29
|
+
// The same data can be obtained by issuing one `^filter:(field=value)`
|
|
30
|
+
// request per dimension value and stitching the single-series payloads
|
|
31
|
+
// together client-side. Filter-only requests don't trigger the bug.
|
|
32
|
+
//
|
|
33
|
+
// Returns an array of { path, label } objects. When no dimension is present,
|
|
34
|
+
// returns a single entry with the original path and a null label.
|
|
35
|
+
function expandDimensionToFilters(reportsPath) {
|
|
36
|
+
var url;
|
|
37
|
+
try {
|
|
38
|
+
url = new URL(reportsPath, window.location.origin);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return [{ path: reportsPath, label: null }];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var nameParam = url.searchParams.get("name");
|
|
44
|
+
if (!nameParam) {
|
|
45
|
+
return [{ path: reportsPath, label: null }];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var parts = nameParam.split("^");
|
|
49
|
+
var baseName = parts[0];
|
|
50
|
+
var clauses = parts.slice(1);
|
|
51
|
+
|
|
52
|
+
var dimensionField = null;
|
|
53
|
+
var dimensionValues = [];
|
|
54
|
+
var otherClauses = [];
|
|
55
|
+
|
|
56
|
+
clauses.forEach(function (c) {
|
|
57
|
+
var m = c.match(/^dimension:([^(]+)\(([^)]+)\)$/);
|
|
58
|
+
if (m && dimensionField === null) {
|
|
59
|
+
dimensionField = m[1];
|
|
60
|
+
dimensionValues = m[2].split("|");
|
|
61
|
+
} else {
|
|
62
|
+
otherClauses.push(c);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (dimensionField === null || dimensionValues.length === 0) {
|
|
67
|
+
return [{ path: reportsPath, label: null }];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If there is also a filter on the same field (e.g. filter:(currency=USD|EUR)),
|
|
71
|
+
// intersect it with the dimension values so the filter still narrows the
|
|
72
|
+
// displayed series. Otherwise we'd silently re-introduce values the user
|
|
73
|
+
// filtered out.
|
|
74
|
+
var sameFieldFilterValues = null;
|
|
75
|
+
otherClauses.forEach(function (c) {
|
|
76
|
+
var fm = c.match(/^filter:\((.+)\)$/);
|
|
77
|
+
if (!fm) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Filter syntax is repeated `field=value` pairs joined by `|`,
|
|
81
|
+
// e.g. `currency=USD|currency=EUR` (NOT `currency=USD|EUR`).
|
|
82
|
+
var pairs = fm[1].split("|");
|
|
83
|
+
var values = [];
|
|
84
|
+
var matched = false;
|
|
85
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
86
|
+
var eq = pairs[i].indexOf("=");
|
|
87
|
+
if (eq === -1) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
var field = pairs[i].substring(0, eq);
|
|
91
|
+
var value = pairs[i].substring(eq + 1);
|
|
92
|
+
if (field === dimensionField) {
|
|
93
|
+
matched = true;
|
|
94
|
+
values.push(value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (matched) {
|
|
98
|
+
sameFieldFilterValues = (sameFieldFilterValues || []).concat(values);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (sameFieldFilterValues) {
|
|
103
|
+
dimensionValues = dimensionValues.filter(function (v) {
|
|
104
|
+
return sameFieldFilterValues.indexOf(v) !== -1;
|
|
105
|
+
});
|
|
106
|
+
if (dimensionValues.length === 0) {
|
|
107
|
+
// Filter excluded every dimension value — nothing to render.
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return dimensionValues.map(function (value) {
|
|
113
|
+
// Drop the same-field filter clause — we replace it with a single-value
|
|
114
|
+
// filter for this fan-out request. Filters on other fields are kept.
|
|
115
|
+
var preservedClauses = otherClauses.filter(function (c) {
|
|
116
|
+
var fm = c.match(/^filter:\((.+)\)$/);
|
|
117
|
+
if (!fm) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return fm[1].indexOf(dimensionField + "=") === -1;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
preservedClauses.push("filter:(" + dimensionField + "=" + value + ")");
|
|
124
|
+
|
|
125
|
+
var newName = baseName + "^" + preservedClauses.join("^");
|
|
126
|
+
var newUrl = new URL(url.toString());
|
|
127
|
+
newUrl.searchParams.set("name", newName);
|
|
128
|
+
|
|
129
|
+
return { path: newUrl.toString(), label: value };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Merge an array of single-series plugin responses (one per dimension value)
|
|
134
|
+
// into a single multi-series payload compatible with the existing renderer.
|
|
135
|
+
function mergeFanOutResults(results) {
|
|
136
|
+
var firstValid = null;
|
|
137
|
+
for (var i = 0; i < results.length; i++) {
|
|
138
|
+
var r = results[i];
|
|
139
|
+
if (r && r.json && r.json[0]) {
|
|
140
|
+
firstValid = r;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (!firstValid) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var merged = {
|
|
149
|
+
name: firstValid.json[0].name,
|
|
150
|
+
type: firstValid.json[0].type,
|
|
151
|
+
data: [],
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
results.forEach(function (r) {
|
|
155
|
+
if (!r || !r.json || !r.json[0] || !r.json[0].data) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
r.json[0].data.forEach(function (series) {
|
|
159
|
+
merged.data.push({
|
|
160
|
+
// Use the dimension value as the series name so the chart legend
|
|
161
|
+
// distinguishes USD vs EUR vs GBP etc.
|
|
162
|
+
name: r.label || series.name,
|
|
163
|
+
values: series.values,
|
|
164
|
+
value: series.value,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return merged;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function renderPayload(data) {
|
|
173
|
+
var renderer = new Kiddo.Renderer("#chartAnchor");
|
|
174
|
+
|
|
175
|
+
if (data === undefined || data.data === undefined || data.data.length == 0) {
|
|
176
|
+
return renderer.noData();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
switch (data.type) {
|
|
181
|
+
case "COUNTERS":
|
|
182
|
+
renderer.pieChart(data);
|
|
183
|
+
break;
|
|
184
|
+
case "TIMELINE":
|
|
185
|
+
renderer.lineChart(data);
|
|
186
|
+
// Date controls only make sense for timelines
|
|
187
|
+
$("#date-controls").show();
|
|
188
|
+
break;
|
|
189
|
+
case "TABLE":
|
|
190
|
+
renderer.table(data);
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
console.log("No such type implemented: " + data.type);
|
|
194
|
+
renderer.noData();
|
|
195
|
+
}
|
|
196
|
+
} catch (ex) {
|
|
197
|
+
console.log(ex);
|
|
198
|
+
renderer.noData();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
24
202
|
$(document).ready(function () {
|
|
25
203
|
if ($("#chartAnchor").length == 0) {
|
|
26
204
|
return;
|
|
27
205
|
}
|
|
28
206
|
|
|
29
|
-
|
|
207
|
+
var reportsPath = $("#chartAnchor").data("reports-path");
|
|
208
|
+
var requests = expandDimensionToFilters(reportsPath);
|
|
209
|
+
|
|
210
|
+
if (window.console && console.info) {
|
|
211
|
+
console.info(
|
|
212
|
+
"[kanaui] dimension fan-out:",
|
|
213
|
+
requests.length,
|
|
214
|
+
"request(s)",
|
|
215
|
+
requests.map(function (r) {
|
|
216
|
+
return r.label;
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (requests.length === 0) {
|
|
30
222
|
$("#loading-spinner").remove();
|
|
223
|
+
return new Kiddo.Renderer("#chartAnchor").noData();
|
|
224
|
+
}
|
|
31
225
|
|
|
32
|
-
|
|
226
|
+
var fetchAll = Promise.all(
|
|
227
|
+
requests.map(function (req) {
|
|
228
|
+
return d3.json(req.path).then(function (json) {
|
|
229
|
+
return { label: req.label, json: json };
|
|
230
|
+
});
|
|
231
|
+
})
|
|
232
|
+
);
|
|
33
233
|
|
|
34
|
-
|
|
234
|
+
fetchAll
|
|
235
|
+
.then(function (results) {
|
|
236
|
+
$("#loading-spinner").remove();
|
|
35
237
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
238
|
+
var data;
|
|
239
|
+
if (results.length === 1 && results[0].label === null) {
|
|
240
|
+
// No dimension fan-out — preserve original single-request behavior.
|
|
241
|
+
data = results[0].json && results[0].json[0];
|
|
242
|
+
} else {
|
|
243
|
+
data = mergeFanOutResults(results);
|
|
244
|
+
}
|
|
43
245
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
case "COUNTERS":
|
|
47
|
-
renderer.pieChart(data);
|
|
48
|
-
break;
|
|
49
|
-
case "TIMELINE":
|
|
50
|
-
renderer.lineChart(data);
|
|
51
|
-
// Date controls only make sense for timelines
|
|
52
|
-
$("#date-controls").show();
|
|
53
|
-
break;
|
|
54
|
-
case "TABLE":
|
|
55
|
-
renderer.table(data);
|
|
56
|
-
break;
|
|
57
|
-
default:
|
|
58
|
-
console.log("No such type implemented: " + type);
|
|
59
|
-
renderer.noData();
|
|
246
|
+
if (!data) {
|
|
247
|
+
return new Kiddo.Renderer("#chartAnchor").noData();
|
|
60
248
|
}
|
|
61
|
-
};
|
|
62
249
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
renderer.noData();
|
|
68
|
-
}
|
|
69
|
-
}).catch(function (error) {
|
|
70
|
-
$("#loading-spinner").remove();
|
|
250
|
+
renderPayload(data);
|
|
251
|
+
})
|
|
252
|
+
.catch(function (error) {
|
|
253
|
+
$("#loading-spinner").remove();
|
|
71
254
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
255
|
+
var renderer = new Kiddo.Renderer("#chartAnchor");
|
|
256
|
+
renderError(errorMessage(error));
|
|
257
|
+
return renderer.noData();
|
|
258
|
+
});
|
|
76
259
|
});
|
|
77
260
|
})(d3, jQuery, window, document);
|
data/lib/kanaui/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kanaui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kill Bill core team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06
|
|
11
|
+
date: 2026-09-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bootstrap-datepicker-rails
|
|
@@ -106,14 +106,14 @@ dependencies:
|
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '7.
|
|
109
|
+
version: '7.2'
|
|
110
110
|
type: :runtime
|
|
111
111
|
prerelease: false
|
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '7.
|
|
116
|
+
version: '7.2'
|
|
117
117
|
- !ruby/object:Gem::Dependency
|
|
118
118
|
name: spinjs-rails
|
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -203,7 +203,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
203
203
|
requirements:
|
|
204
204
|
- - ">="
|
|
205
205
|
- !ruby/object:Gem::Version
|
|
206
|
-
version:
|
|
206
|
+
version: 3.1.0
|
|
207
207
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
208
|
requirements:
|
|
209
209
|
- - ">="
|