Analysing EC Builds
I was looking for a way to analyse EC Builds in an easy way to identify where potential bottlenecks within the buildprocess could be. I have created a python script which exports EC build logs (can only export latest build logs) to JSON and converts these into CSVs which can be imported into an ElastiCube. So I have created a simple dashboard and EC-model. Feel free to adjust to your own needs and perhaps share them so I can learn aswell 🙂 (Do not forget to change the folder in de CSV-connection to the folder where you saved your CSVs) Download ECmodel + Dashboard: LINK Download Python: LINK Configure the settings.yml file with your admin credentials and sisense url. I am not a python-king so adjust freely if you see something thats worth improving.3.7KViews7likes1CommentColumn chart to WordCloud
Here is a script to convert a Column chart to WordCloud chart Reference: https://www.binextlevel.com/post/wordcloud-from-column-chart Create a Column chart. It should contain only one field as category and one Value panel and apply below script. (function(m) { "object" === typeof module && module.exports ? module.exports = m : m(Highcharts) })(function(m) { var w = function() { return function(d) { var n = this, k = n.graphic, p = d.animate, m = d.attr, q = d.onComplete, z = d.css, r = d.group, v = d.renderer, D = d.shapeArgs; d = d.shapeType; n.shouldDraw() ? (k || (n.graphic = k = v[d](D).add(r)), k.css(z).attr(m).animate(p, void 0, q)) : k && k.animate(p, void 0, function() { n.graphic = k = k.destroy(); "function" === typeof q && q() }); k && k.addClass(n.getClassName(), !0) } }(); (function(d, n) { var k = d.each, p = d.extend, m = d.isArray, q = d.isNumber, z = d.isObject, r = d.Series, v = function(a, b) { return !(b.left > a.right || b.right < a.left || b.top > a.bottom || b.bottom < a.top) }, D = function(a, b) { var c = !1, g = a.rect, f; a.lastCollidedWith && (f = a.lastCollidedWith.rect, (c = v(g, f)) || delete a.lastCollidedWith); c || (c = !!d.find(b, function(b) { var c; f = b.rect; if (c = v(g, f)) a.lastCollidedWith = b; return c })); return c }, B = function(a) { var b = Math.ceil((Math.sqrt(a) - 1) / 2), c = 2 * b + 1, g = Math.pow(c, 2), f = !1, c = c - 1; 1E4 >= a && ("boolean" === typeof f && a >= g - c && (f = { x: b - (g - a), y: -b }), g -= c, "boolean" === typeof f && a >= g - c && (f = { x: -b, y: -b + (g - a) }), g -= c, "boolean" === typeof f && (f = a >= g - c ? { x: -b + (g - a), y: b } : { x: b, y: b - (g - a - c) }), f.x *= 5, f.y *= 5); return f }, w = function(a, b) { a /= b; return { width: 256 * a, height: 256, ratio: a } }, C = function(a, b, c) { return b + (c - b) / (a - 1) * Math.floor(Math.random() * a) }, G = function(a, b) { a = a.getBBox(); var c = b.width / 2, g = -(b.height / 2), f = b.height / 2; return !(-(b.width / 2) < a.x && c > a.x + a.width && g < a.y && f > a.y + a.height) }; d.seriesType("wordcloud", "column", { animation: { duration: 500 }, borderWidth: 0, clip: !1, colorByPoint: !0, placementStrategy: "center", rotation: { from: 0, orientations: 2, to: 90 }, showInLegend: !1, spiral: "rectangular", style: { fontFamily: "sans-serif", fontWeight: "900" }, tooltip: { followPointer: !0, pointFormat: '\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.weight}\x3c/b\x3e\x3cbr/\x3e' } }, { animate: r.prototype.animate, bindAxes: function() { var a = { endOnTick: !1, gridLineWidth: 0, lineWidth: 0, maxPadding: 0, startOnTick: !1, title: null, tickPositions: [] }; r.prototype.bindAxes.call(this); p(this.yAxis.options, a); p(this.xAxis.options, a) }, deriveFontSize: function(a) { return Math.floor(25 * a) }, drawPoints: function() { var a = this, b = a.hasRendered, c = a.xAxis, g = a.yAxis, f = a.group, d = a.options, m = d.animation, n = a.chart.renderer, A = n.text().add(f), r = [], v = a.placementStrategy[d.placementStrategy], B = a.spirals[d.spiral], C = d.rotation, H = a.points.map(function(a) { return a.weight }), I = Math.max.apply(null, H), t = w(c.len, g.len), E = a.points.sort(function(a, b) { return b.weight - a.weight }); k(E, function(c) { var g = p({ fontSize: a.deriveFontSize(1 / I * c.weight) + "px", fill: c.color }, d.style), l = v(c, { data: E, field: t, placed: r, rotation: C }), h = { align: "center", x: l.x, y: l.y, text: c.name, rotation: l.rotation }, k, x, e; A.css(g).attr(h); c.clientRect = e = p({}, A.element.getBoundingClientRect()); x = A; for (var w = t, F = 1, y = { x: 0, y: 0 }, u = c.rect = p({}, e); (D(c, r) || G(x, w)) && !1 !== y;) y = B(F, { field: w }), z(y) && (u.left = e.left + y.x, u.right = u.left + u.width, u.top = e.top + y.y, u.bottom = u.top + u.height), F++; x = y; if (z(x)) { h.x += x.x; h.y += x.y; p(l, { left: h.x - e.width / 2, right: h.x + e.width / 2, top: h.y - e.height / 2, bottom: h.y + e.height / 2 }); e = t; if (!q(e.left) || e.left > l.left) e.left = l.left; if (!q(e.right) || e.right < l.right) e.right = l.right; if (!q(e.top) || e.top > l.top) e.top = l.top; if (!q(e.bottom) || e.bottom < l.bottom) e.bottom = l.bottom; t = e; r.push(c); c.isNull = !1 } else c.isNull = !0; m && (k = { x: h.x, y: h.y }, b ? (delete h.x, delete h.y) : (h.x = 0, h.y = 0)); c.draw({ animate: k, attr: h, css: g, group: f, renderer: n, shapeArgs: void 0, shapeType: "text" }) }); A = A.destroy(); c = Math.min(1 / (2 * Math.max(Math.abs(t.left), Math.abs(t.right))) * c.len, 1 / (2 * Math.max(Math.abs(t.top), Math.abs(t.bottom))) * g.len); a.group.attr({ scaleX: c, scaleY: c }) }, hasData: function() { return z(this) && !0 === this.visible && m(this.points) && 0 < this.points.length }, placementStrategy: { random: function(a, b) { a = b.field; b = b.rotation; return { x: Math.round(a.width * (Math.random() + .5) / 2) - a.width / 2, y: Math.round(a.height * (Math.random() + .5) / 2) - a.height / 2, rotation: C(b.orientations, b.from, b.to) } }, center: function(a, b) { a = b.rotation; return { x: 0, y: 0, rotation: C(a.orientations, a.from, a.to) } } }, pointArrayMap: ["weight"], spirals: { archimedean: function(a, b) { var c = b.field; b = !1; var c = c.width * c.width + c.height * c.height, d = .2 * a; 1E4 >= a && (b = { x: d * Math.cos(d), y: d * Math.sin(d) }, Math.min(Math.abs(b.x), Math.abs(b.y)) < c || (b = !1)); return b }, rectangular: function(a, b) { a = B(a, b); b = b.field; a && (a.x *= b.ratio); return a }, square: B }, getPlotBox: function() { var a = this.chart, b = a.inverted, c = this[b ? "yAxis" : "xAxis"], b = this[b ? "xAxis" : "yAxis"]; return { translateX: (c ? c.left : a.plotLeft) + (c ? c.len : a.plotWidth) / 2, translateY: (b ? b.top : a.plotTop) + (b ? b.len : a.plotHeight) / 2, scaleX: 1, scaleY: 1 } } }, { draw: n, shouldDraw: function() { return !this.isNull } }) })(m, w) }); widget.on('processresult', function(widget,ev) { ev.result.chart.type = 'wordcloud' ev.result.plotOptions['wordcloud'] = ev.result.plotOptions['column'] delete ev.result.plotOptions['column'] ev.result.tooltip.enabled = true ev.result.plotOptions.wordcloud.rotation = {orientations:0} wordCloudArr = [] $.each(ev.result.series[0].data, function(index, value){ value.weight = value.y wordCloudArr.push({'name':value.selectionData[0], 'weight':value.y}) }) ev.result.series[0].data = wordCloudArr }) widget.on('beforedatapointtooltip', function(widget,ev) { ev.cancel = true }) -Hari5KViews6likes5CommentsDisplay widget in tooltip
By default values and labels are displayed in tooltip. Also its possible to add additional information to tooltip by using script . Sometimes it would be more helpful to display a widget as tooltip (Reference) Steps: Create base chart - the chart in which we need to add tooltip. Create a dashboard and add a widget that needs to be displayed as tooltip. Size of widget in this dashboard should be small enough to fit in to the tooltip. Add below script to base chart and update the variable 'dashboaardurl' with URL of dashboard we created for tooltip. var category widget.on('beforedatapointtooltip', function(se, ev){ dashboardurl = 'https://XXXX.sisense.com/app/main#/dashboards/6612346ac77683002ea37645' redrawYN = category != ev.context.category category = ev.context.category filterPanel = ev.widget.metadata.panels[0].items[0].jaql filterPanel.filter = { explicit: true, members: [category], multiSelection: true } filterjaql = {jaql: filterPanel} var filtersArray = [filterjaql]; var filterString = JSON.stringify(filtersArray); var uriEncoded = encodeURIComponent(filterString); ev.template = `<iframe width="100%" frameborder="0" src="${dashboardurl}?embed=true&r=false&filter=${uriEncoded}"></iframe>` if(redrawYN) se.redraw() }) Note: Supported widget - Line chart, Column chart, Bar chart, Pie chart Currently this script doesn't support widget with 'breakby' panel. Tooltip may take few seconds to load as it is actually a dashboard. -Hari4.8KViews6likes3CommentsJump to Dashboard arrow mark superimposed with widget title
Hi Team, I have created a Jump to Dashboard from a widget. However, I am seeing from couple of months that the arrow signal specifying the JTD widget is now superimposed with the title of the widget leaving it very clumsy. Any help regarding the same will be very helpful.3KViews5likes4CommentsBar Chart - Add Percentage to the Value Label
When creating a column chart you might want to add a percentage value next to the actual value Add the following script to your widget: widget.on('processresult', function(widget,result) { result.result.plotOptions.series.dataLabels.formatter = function() { total = 0 this.series.data.forEach(function(datapoint) { total += datapoint.y }) percent = (100 * this.y) / (total); percent = percent.toFixed(1); return this.y + '<br/>(' + percent + '%)' } }) Before After2.8KViews5likes3CommentsThe Sisense Widgetscript Library
Hey Hey, There have been a lot of widgetscripts written to solve certain challenges in Sisense. It can sometimes be a hassle to find the right one and also know if they still work. For this reason I have created this post. This way we can create a nice overview of widget scripts 🙂 Things to keep in consideration Every script has a description, a link to the original post and a working with version X (to determine if it works for certain versions of Sisense) If you have a script you would like on this list, reply to this post with the name, description and a link to the original post (if it is your script or it does not exist, create a post) If you have a question regarding the script, please click the link to go to the original post and ask your question there. This way we can keep this post clean 🙂 Scripts: Title Description Link Works in version Filter buttons in widget A widgetscript that adds filter buttons to a widget for users to easily filter the chart Link L2024.2 Format pivot2 fonts A script to format pivot fonts (size, color etc) Link L2024.2 Using BloX To Dynamically Change A Dimension Using BloX To Dynamically Change A Dimension Link L2024.2 Flip Legend on Stacked Bar Chart Link L2024.2 Highlight Max and Min values in Line chart Sometimes line chart contains more value points and it wont be readable if we enable the value labels. But it would be a nice feature to highlight minimum and maximum value in the chart to get a better understanding of value range in it. Link L2024.2 Remove a pre-defined filter option from the list Link L2024.11KViews5likes0CommentsWhat is the best way to drive adoption and collaboration within the Sisense application?
Hi there, I think the subject speaks for itself. We are an OEM in Healthcare and are sometimes experiencing 'hurdles' when it comes to increasing adoption and collaboration within Sisense by healthcare professionals. We talk a lot with different end-users and try to understand what they need and when they need it. This works great and gave us a lot of insight and ideas for new dashboards. However there remains a group of people within every customer that do not see the benefits. How did you overcome these? Would love to hear some tips Hamza2.3KViews4likes1CommentEnable/Disable Pivot columns based on selected filter
Sometimes we need to hide or show few columns in pivot table based on a filter selected. Here is a script to achieve this. Reference: https://www.binextlevel.com/post/enable-disable-pivot-columns-based-on-selected-filter Steps: Create a Pivot table Add panels to enable/disable based on selected filter and disable them. It will enable dynamically based on selection. Add below script to widget Update the variable 'ItemList' with mapping of filter item to name of panels to be enabled. Also update the variable 'filterName' with name of dashboard filter based on which the columns enable/disable Save the script and refresh dashboard. let filterName = 'Region' let ItemList = { 'South':['County','Gender', 'City'], 'Northeast':['Gender', 'State'], 'Default':['City', 'State'] } widget.on("buildquery", function (se, ev) { let filter = se.dashboard.filters.$$items.find(el=>el.jaql.title == filterName) let filterItem = filter.jaql.filter.members[0] let columnList = ItemList[filterItem] if(columnList == null || columnList == undefined) columnList = ItemList['Default'] $.each(ev.widget.metadata.panels[0].items, function(index, value){ if(value.disabled == true && columnList.includes(value.jaql.title) ) { var newJaql = { jaql : JSON.parse(JSON.stringify(value.jaql)) } ev.query.metadata.push(newJaql) lastIndex = ev.query.metadata.length - 1 ev.query.metadata[lastIndex].disabled = false ev.query.metadata[lastIndex].panel = 'rows' } }) $.each(ev.widget.metadata.panels[1].items, function(index, value){ if(value.disabled == true && columnList.includes(value.jaql.title) ) { var newJaql = { jaql : JSON.parse(JSON.stringify(value.jaql)) } ev.query.metadata.push(newJaql) lastIndex = ev.query.metadata.length - 1 ev.query.metadata[lastIndex].disabled = false ev.query.metadata[lastIndex].panel = 'measures' } }) }) -Hari2KViews4likes1CommentCustom No Result message
Below script will allow you to change default 'No Result' message to any message you want. It is possible to apply different messages to each widget. Refer: https://www.binextlevel.com/post/custom-no-result-message widget.on('domready', function(se){ no_result_message = 'No sales data available' no_result_url = 'https://thumbs.dreamstime.com/z/no-found-symbol-unsuccessful-search-vecotr-upset-magnifying-glass-cute-not-zoom-icon-suitable-results-oops-page-failure-122786031.jpg' if(se.$noResults) { if (prism.activeWidget == null) no_result_container = $(`widget[widgetid="${se.oid}"]`) else no_result_container = $('.prism-widget-preview') if(no_result_message.length > 0) { $(no_result_container).find('.widget-no-result-overlay .widget-no-result-notify-holder .no-result-title').text(no_result_message) } if(no_result_url.length > 0) { $(no_result_container).find('.widget-no-result-overlay .widget-no-result-notify-holder .image').attr('src', no_result_url) $(no_result_container).find('.widget-no-result-overlay .widget-no-result-notify-holder .image').css('width', 'auto') } } }) -Hari2.5KViews4likes1Comment