kanaui 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5028db371172c24fb078fdc217e985e058cfbecf
4
- data.tar.gz: da0922bc0da3a84267d30f0f80138a23fa9f92c9
3
+ metadata.gz: b509920c148aef35ccf0bffbe38b935b669a35ff
4
+ data.tar.gz: e5d121e521e55c1c3f882f7f0bbfd576c961a7e9
5
5
  SHA512:
6
- metadata.gz: 9fc14b185e6631a093a74bee47b1825b7fd04aa4dbc7cbc519318e24657cd721168fb70304e5cc3859bd3a2474d499f7c8d45dbbf8f2d18b2688f2d2c6241b6a
7
- data.tar.gz: ce0be1bf02f07f8173a4c99f7b389408ffc5f7f8eef47233e970753e5811f550ae05f650d3eea74089f30b52982183f4ba6f447c437f9da6049accb8938249ee
6
+ metadata.gz: e8ebbb2db468390719e6c125880f483382f2cfd61ca4256128409c7dfa89aa11b367d3f6b5bdc039897c81de3fd627007348ff2c86e82096e94392c4c7f47b4d
7
+ data.tar.gz: bc6c3397d0fa1f6192f81cf85c199934126c4a42283791ec8e6ec7b0b8e31dc7678804dc5c288474713f22a9ccad7dac089967a2a6fed42440b5edb83956bc81
@@ -13,7 +13,7 @@
13
13
  return d3.svg.axis()
14
14
  .scale(self.y)
15
15
  .orient("left")
16
- .tickFormat(d3.format('d'));
16
+ .tickFormat(d3.format(',d'));
17
17
  }
18
18
 
19
19
  var xAxis = makeXAxis();
@@ -23,32 +23,32 @@
23
23
  x: xAxis,
24
24
  y: yAxis,
25
25
  render: function(svg, yTitle){
26
- svg.append("g")
27
- .attr("class", "x axis")
28
- .attr("transform", "translate(" + self.margin_left + "," + self.height + ")")
29
- .call(xAxis);
30
-
31
- svg.append("g")
32
- .attr("class", "y axis")
33
- .attr("transform", "translate(" + self.margin_left + ",0)")
34
- .call(yAxis)
35
-
36
- .append("text")
37
- .attr("transform", "rotate(-90)")
38
- .attr("y", 6)
39
- .attr("dy", ".71em")
40
- .style("text-anchor", "end")
41
- .text(yTitle);
26
+ svg.append("g")
27
+ .attr("class", "grid")
28
+ .attr("transform", "translate(" + self.margin_left + "," + self.height + ")")
29
+ .call(makeXAxis().tickSize(-self.height, 0, 0).tickFormat(""));
30
+
31
+ svg.append("g")
32
+ .attr("class", "grid")
33
+ .attr("transform", "translate(" + self.margin_left + ",0)")
34
+ .call(makeYAxis().tickSize(-self.width, 0, 0).tickFormat(""));
42
35
 
43
36
  svg.append("g")
44
- .attr("class", "grid")
45
- .attr("transform", "translate(" + self.margin_left + "," + self.height + ")")
46
- .call(makeXAxis().tickSize(-self.height, 0, 0).tickFormat(""));
47
-
48
- svg.append("g")
49
- .attr("class", "grid")
50
- .attr("transform", "translate(" + self.margin_left + ",0)")
51
- .call(makeYAxis().tickSize(-self.width, 0, 0).tickFormat(""));
37
+ .attr("class", "x axis")
38
+ .attr("transform", "translate(" + self.margin_left + "," + self.height + ")")
39
+ .call(xAxis);
40
+
41
+ svg.append("g")
42
+ .attr("class", "y axis")
43
+ .attr("transform", "translate(" + self.margin_left + ",0)")
44
+ .call(yAxis);
45
+ //.append("text")
46
+ //.attr("transform", "rotate(-90)")
47
+ //.attr("y", 6)
48
+ //.attr("dy", ".71em")
49
+ //.style("text-anchor", "end")
50
+ //.text(yTitle);
51
+
52
52
  }
53
53
  };
54
54
  };
@@ -30,11 +30,16 @@
30
30
 
31
31
  self.x.domain(x_domain);
32
32
 
33
- var y_domain = [0, d3.max(datasets, function(datum){
34
- return d3.max(datum.values, function(d){
35
- return d.y;
36
- });
37
- })];
33
+ var y_domain = [d3.min(datasets, function (datum) {
34
+ return d3.min(datum.values, function (d) {
35
+ return d.y;
36
+ });
37
+ }),
38
+ d3.max(datasets, function (datum) {
39
+ return d3.max(datum.values, function (d) {
40
+ return d.y;
41
+ });
42
+ })];
38
43
 
39
44
  self.y.domain(y_domain);
40
45
 
@@ -47,8 +52,8 @@
47
52
  name = dataset.name;
48
53
 
49
54
  data.forEach(function(d) {
50
- d.date = d.x;
51
- d.x = helper.parseDate(d.x);
55
+ d.date = d.x.split('T')[0]; // Support both date and date/times
56
+ d.x = helper.parseDate(d.date);
52
57
  d.y = +d.y;
53
58
  });
54
59
 
@@ -12,23 +12,13 @@
12
12
  .style("display", "none");
13
13
 
14
14
  var canvas = svg.append("g")
15
+ .attr("id", "mouseover_canvas")
15
16
  .style("display", "none");
16
17
 
17
18
  var info = canvas.append("rect")
18
19
  .attr("class", "information")
19
20
  .attr("width", self.width / 2);
20
21
 
21
- var addInfoDimensions = function(element){
22
- var box = element.node().getBBox();
23
- var infoBox = info.node().getBBox();
24
- var margin = 10;
25
-
26
- info.attr("height", infoBox.height + box.height + margin);
27
- if(infoBox.width < box.width){
28
- info.attr("width", box.width + margin);
29
- }
30
- };
31
-
32
22
  // The magic:
33
23
  svg.append("rect")
34
24
  .attr("class", "overlay")
@@ -41,45 +31,49 @@
41
31
 
42
32
  var infoTitleBg = canvas.append("rect")
43
33
  .attr("class", "info-title__bg")
44
- .attr("width", self.width / 2)
34
+ .attr("width", self.width)
45
35
  .attr("height", 30);
46
36
 
47
- canvas.append("text")
48
- .attr("dy", ".85em")
49
- .attr("dx", 100)
37
+ var infoTitle = canvas.append("text")
38
+ .attr("dy", "1em")
39
+ .attr("dx", 0)
50
40
  .attr("class", "info-title")
51
41
  .attr("id", "info-title");
52
42
 
53
- addInfoDimensions(infoTitleBg);
43
+ var addInfoDimensions = function(element) {
44
+ // On mouseover, element is the current label_idx
45
+ var box = element.node().getBBox();
46
+ // infoBox is the .information rect
47
+ var infoBox = info.node().getBBox();
48
+ var infoTitleBox = infoTitle.node().getBBox();
49
+ var margin = 40;
50
+
51
+ info.attr("height", infoBox.height + box.height + 7);
52
+ if (infoBox.width < box.width) {
53
+ info.attr("width", box.width + margin);
54
+ infoTitleBg.attr("width", box.width + margin);
55
+
56
+ $('#mouseover_canvas #info-title').attr("dx", (box.width + margin) / 2 - infoTitleBox.width / 2);
57
+ }
58
+ };
54
59
 
55
60
  self.datasets.forEach(function(element, index){
56
61
  focus.append("circle")
57
62
  .attr("r", 4.5)
58
63
  .attr("id", "circle_" + index)
59
64
  .attr("transform", "translate(" + self.margin_left + ",0)");
60
-
61
- canvas.append("circle")
62
- .attr("r", 5.5)
63
- .attr("cx", 10)
64
- .attr("cy", (index + 2) * 25)
65
- .style("fill", self.color(element.name))
66
- .style("stroke", "black")
67
-
68
- var text = canvas.append("text")
69
- .attr("y", (index + 2) * 25)
70
- .attr("x", 20)
71
- .attr("cx", 20)
72
- .attr("dy", ".35em")
73
- .attr("class", "chart_values")
74
- .attr("id", "label_" + index)
75
- .text(element.name);
76
-
77
- addInfoDimensions(text);
78
65
  });
79
66
 
80
67
  function mousemove() {
81
68
  var _this = this;
82
69
 
70
+ $('#mouseover_canvas .chart_values').detach().remove();
71
+ $('#mouseover_canvas .chart_circles').detach().remove();
72
+ info.attr("height", infoTitleBg.node().getBBox().height + 10);
73
+ info.attr("width", 1);
74
+ infoTitle.attr("width", 1);
75
+
76
+ var elementsForLegend = [];
83
77
  self.datasets.forEach(function(element, index){
84
78
  var data = element.values;
85
79
  var name = element.name;
@@ -87,16 +81,24 @@
87
81
  var x0 = x.invert(d3.mouse(_this)[0]),
88
82
  i = helper.bisectDate(data, x0, 1),
89
83
  d0 = data[i - 1],
90
- d1 = data[i],
91
- d = x0 - d0.x > d1.x - x0 ? d1 : d0;
84
+ d1 = data[i];
85
+
86
+ if (d0 !== undefined && d1 !== undefined) {
87
+ var d = x0 - d0.x > d1.x - x0 ? d1 : d0;
88
+ } else if (d0 !== undefined) {
89
+ var d = d0;
90
+ } else if (d1 !== undefined) {
91
+ var d = d1;
92
+ } else {
93
+ return;
94
+ }
92
95
 
93
96
  focus.select("#circle_" + index)
94
97
  .attr("cx", x(d.x))
95
98
  .attr("cy", y(d.y))
96
99
  .style("fill", self.color(name));
97
100
 
98
- var infoWidth = info.node().getBBox().width
99
- var canvasPosition = x(x0) > self.width / 2 ? 50 : self.width - infoWidth;
101
+ var canvasPosition = x(x0) > self.width / 2 ? 50 : self.width / 2;
100
102
 
101
103
  canvas
102
104
  .attr("transform", "translate(" + canvasPosition + ",0)");
@@ -104,7 +106,33 @@
104
106
  canvas.select("#info-title")
105
107
  .text(d.date);
106
108
 
107
- text = canvas.select("#label_" + index).text(helper.formatValueDisplay(name, d));
109
+ elementsForLegend.push({element: element, d: d});
110
+ });
111
+
112
+ elementsForLegend.sort(function (a, b) {
113
+ return a.d.y > b.d.y ? -1 : (a.d.y < b.d.y ? 1 : 0);
114
+ });
115
+
116
+ // Limit the number of legend items (document largest values only)
117
+ elementsForLegend.slice(0,10).forEach(function(element, index) {
118
+ canvas.append("circle")
119
+ .attr("r", 5.5)
120
+ .attr("cx", 15)
121
+ .attr("cy", (index + 2) * 25)
122
+ .attr("class", "chart_circles")
123
+ .style("fill", self.color(element.element.name))
124
+ .style("stroke", "black")
125
+
126
+ var text = canvas.append("text")
127
+ .attr("y", (index + 2) * 25)
128
+ .attr("x", 25)
129
+ .attr("cx", 25)
130
+ .attr("dy", ".35em")
131
+ .attr("class", "chart_values")
132
+ .attr("id", "label_" + index)
133
+ .text(element.d === undefined ? element.element.name : helper.formatValueDisplay(element.element.name, element.d));
134
+
135
+ addInfoDimensions(text);
108
136
  });
109
137
  }
110
138
  }
@@ -3,40 +3,48 @@
3
3
  if($('#chartAnchor').length == 0) { return; }
4
4
 
5
5
  d3.json($('#chartAnchor').data('reports-path'), function(error, json){
6
- if(error){ throw error };
6
+ $('#loading-spinner').remove();
7
+
8
+ var renderer = new Kiddo.Renderer('#chartAnchor');
9
+
10
+ if (error) {
11
+ console.log(error);
12
+ return renderer.noData();
13
+ }
7
14
 
8
15
  var data = json[0];
9
16
 
10
- $('#loading-spinner').remove();
17
+ if (data === undefined ||
18
+ data.data === undefined ||
19
+ data.data.length == 0) {
20
+ return renderer.noData();
21
+ }
11
22
 
12
23
  var render = function(type){
13
- if(data.data.length == 0) { return renderer.noData(); }
14
24
  switch(type){
15
25
  case 'COUNTERS':
16
- var renderer = new Kiddo.Renderer('#chartAnchor');
17
- renderer.pieChart(data)
26
+ renderer.pieChart(data);
18
27
  break;
19
28
  case 'TIMELINE':
20
- var renderer = new Kiddo.Renderer('#chartAnchor');
21
29
  renderer.lineChart(data);
30
+ // Date controls only make sense for timelines
31
+ $('#date-controls').show();
22
32
  break;
23
33
  case 'TABLE':
24
- new ReportsDataTables(null).buildTable(data['data'][0], $('#chartAnchor'));
34
+ renderer.table(data);
25
35
  break;
26
36
  default:
27
37
  console.log('No such type implemented: ' + type);
28
- var renderer = new Kiddo.Renderer('#chartAnchor');
29
38
  renderer.noData();
30
39
  }
31
40
  };
32
41
 
33
42
  try{
34
- render(json[0].type);
35
- }catch(ex){
43
+ render(data.type);
44
+ } catch (ex){
36
45
  console.log(ex);
37
46
  renderer.noData();
38
47
  }
39
-
40
48
  });
41
49
  });
42
50
  })(d3, jQuery, window, document);
@@ -9,6 +9,7 @@
9
9
  .append('svg')
10
10
  .attr('width', settings.raw_width)
11
11
  .attr('height', settings.raw_height)
12
+ .attr('style', 'overflow: visible')
12
13
  .append('g')
13
14
  .attr('transform', 'translate(' + settings.margin_left + ',' + settings.margin_top + ')');
14
15
 
@@ -18,11 +19,15 @@
18
19
  chart.render(svg, data);
19
20
  },
20
21
 
21
- pieChart: function(data){
22
- var chart = Kiddo.PieChart.apply(settings);
22
+ pieChart: function(data){var chart = Kiddo.PieChart.apply(settings);
23
23
  chart.render(svg, data);
24
24
  },
25
25
 
26
+ table: function(data){
27
+ svg.node().parentNode.remove();
28
+ new ReportsDataTables(null).buildTable(data['data'][0], $(selector));
29
+ },
30
+
26
31
  noData: function(){
27
32
  svg.append('text')
28
33
  .attr('class', 'chart-info')
@@ -2,7 +2,7 @@
2
2
  Kiddo.Settings = function(){
3
3
  var margin_top = 30,
4
4
  margin_bottom = 40,
5
- margin_left = 30,
5
+ margin_left = 50,
6
6
  margin_right = 150,
7
7
  raw_width = parseInt(this.element.node().getBoundingClientRect().width, 10),
8
8
  raw_height = 500;
@@ -10,7 +10,7 @@
10
10
  }
11
11
 
12
12
  .x.axis path {
13
- display: none;
13
+ #display: none;
14
14
  }
15
15
 
16
16
  .line {
@@ -6,44 +6,64 @@ module Kanaui
6
6
  # the reports and available_reports endpoints below.
7
7
  #
8
8
  def index
9
- raw_reports = Kanaui::DashboardHelper::DashboardApi.available_reports(options)
9
+ raw_reports = Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)
10
10
 
11
- @endDate = params['endDate'] || Date.today.to_s
11
+ @raw_name = (params[:name] || '').split('^')[0]
12
+
13
+ @end_date = params[:end_date] || Date.today.to_s
12
14
 
13
15
  @available_start_dates = start_date_options
14
- @startDate = params['startDate'] || @available_start_dates['Last 6 months']
16
+ @start_date = params[:start_date] || (params[:delta_days].present? ? (@end_date.to_date - params[:delta_days].to_i.day).to_s : @available_start_dates['Last 6 months'])
15
17
 
16
18
  @reports = JSON.parse(raw_reports)
19
+ @report = current_report(@reports) || {}
20
+
21
+ query = build_slice_and_dice_query
22
+
23
+ # Redirect also in case the dates have been updated to avoid any confusion in the view
24
+ if query.present? || params[:start_date].blank? || params[:end_date].blank?
25
+ # TODO Make metrics configurable
26
+ name = query.present? ? "#{params[:name]}#{query}^metric:count" : params[:name]
27
+ redirect_to dashboard_index_path(:start_date => @start_date,
28
+ :end_date => @end_date,
29
+ :name => name,
30
+ :smooth => params[:smooth],
31
+ :sql_only => params[:sql_only],
32
+ :format => params[:format]) and return
33
+ end
34
+
17
35
  render
18
36
  end
19
37
 
20
38
  # Not used anymore as reports are pulled from index
21
39
  def available_reports
22
- available_reports = Kanaui::DashboardHelper::DashboardApi.available_reports(options)
23
- render json: available_reports
40
+ available_reports = Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)
41
+ render :json => available_reports
24
42
  end
25
43
 
26
44
  def reports
27
- format = params['format'] || 'json'
28
- reports = fetch_reports(format)
29
- render json: reports
30
- end
45
+ format = params[:format] || 'json'
46
+ raw_reports = fetch_reports(format)
31
47
 
32
- private
48
+ if format == 'json' && params[:sql_only] != 'true'
49
+ reports = JSON.parse(raw_reports)
33
50
 
34
- def options
35
- user = current_tenant_user
36
- {
37
- :username => user[:username],
38
- :password => user[:password],
39
- :session_id => user[:session_id],
40
- :api_key => user[:api_key],
41
- :api_secret => user[:api_secret]
42
- }
51
+ # Remove reports with empty data
52
+ reports.reject! do |report|
53
+ reject = false
54
+ (report['data'] || []).each { |data| reject = true if (data['values'] || []).empty? && data['value'].blank? }
55
+ reject
56
+ end
57
+ else
58
+ reports = raw_reports
59
+ end
60
+ render :json => reports
43
61
  end
44
62
 
63
+ private
64
+
45
65
  def start_date_options
46
- end_date = @endDate.to_date
66
+ end_date = @end_date.to_date
47
67
  {
48
68
  'Last month' => (end_date - 1.month).to_s,
49
69
  'Last 3 months' => (end_date - 3.months).to_s,
@@ -53,13 +73,53 @@ module Kanaui
53
73
  end
54
74
 
55
75
  def fetch_reports(format)
56
- if params['fake']
76
+ if params[:fake]
57
77
  type = params.fetch('type', 'pie')
58
78
  File.read(Kanaui::Engine.root.join('lib', 'sample_data', "#{type}.json"))
59
79
  else
60
- Kanaui::DashboardHelper::DashboardApi.reports(params['startDate'], params['endDate'], params['name'], params['smooth'], format, options)
80
+ Kanaui::DashboardHelper::DashboardApi.reports(params[:start_date],
81
+ params[:end_date],
82
+ params[:name],
83
+ params[:smooth],
84
+ params[:sql_only],
85
+ format,
86
+ options_for_klient)
61
87
  end
62
88
  end
63
89
 
90
+ def current_report(reports)
91
+ return nil if @raw_name.blank?
92
+
93
+ reports.find { |r| r['reportName'] == @raw_name }
94
+ end
95
+
96
+ def build_slice_and_dice_query
97
+ query = ''
98
+
99
+ filters = {}
100
+ groups = {}
101
+ ((@report['schema'] || {})['fields'] || []).each do |field|
102
+ field_name = field['name']
103
+
104
+ filters[field_name] = params["filter_#{field_name}"]
105
+ groups[field_name] = params["group_#{field_name}"]
106
+ end
107
+
108
+ filter_query = ''
109
+ filters.each do |k, v|
110
+ next if v.blank?
111
+ filter_query << '%26' unless filter_query.blank?
112
+ filter_query << "(#{k}=#{v.join("|#{k}=")})"
113
+ end
114
+ query << "^filter:#{filter_query}" unless filter_query.blank?
115
+
116
+ groups.each do |k, v|
117
+ next if v.blank?
118
+ # TODO Make "no other" configurable
119
+ query << "^dimension:#{k}(#{v.join('|')}|-)"
120
+ end
121
+
122
+ query
123
+ end
64
124
  end
65
125
  end