dash_creator 0.1.2

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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +170 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/dash_creator_manifest.js +2 -0
  6. data/app/assets/javascripts/dash_creator/application.js +24 -0
  7. data/app/assets/javascripts/dash_creator/chart.js +2 -0
  8. data/app/assets/javascripts/dash_creator/dashboard.js +2 -0
  9. data/app/assets/javascripts/dash_creator/dashboard_object.js +2 -0
  10. data/app/assets/javascripts/dash_creator/filter.js +2 -0
  11. data/app/assets/javascripts/dash_creator/libs/Chart.js +12269 -0
  12. data/app/assets/javascripts/dash_creator/libs/bootstrap.js +3535 -0
  13. data/app/assets/javascripts/dash_creator/libs/chartCreator.js +1582 -0
  14. data/app/assets/javascripts/dash_creator/libs/dashboardCreator.js +531 -0
  15. data/app/assets/javascripts/dash_creator/libs/daterangepicker.js +1626 -0
  16. data/app/assets/javascripts/dash_creator/libs/filterCreator.js +733 -0
  17. data/app/assets/javascripts/dash_creator/libs/jquery-ui.js +18706 -0
  18. data/app/assets/javascripts/dash_creator/libs/jquery.minicolors.js +1108 -0
  19. data/app/assets/javascripts/dash_creator/libs/moment.js +4301 -0
  20. data/app/assets/javascripts/dash_creator/libs/tether.js +1811 -0
  21. data/app/assets/javascripts/dash_creator/user.js +2 -0
  22. data/app/assets/stylesheets/dash_creator/application.css +16 -0
  23. data/app/assets/stylesheets/dash_creator/chart.css +4 -0
  24. data/app/assets/stylesheets/dash_creator/dashboard.css +4 -0
  25. data/app/assets/stylesheets/dash_creator/dashboard_object.css +4 -0
  26. data/app/assets/stylesheets/dash_creator/filter.css +4 -0
  27. data/app/assets/stylesheets/dash_creator/fonts/FontAwesome.otf +0 -0
  28. data/app/assets/stylesheets/dash_creator/fonts/fontawesome-webfont.eot +0 -0
  29. data/app/assets/stylesheets/dash_creator/fonts/fontawesome-webfont.svg +2671 -0
  30. data/app/assets/stylesheets/dash_creator/fonts/fontawesome-webfont.ttf +0 -0
  31. data/app/assets/stylesheets/dash_creator/fonts/fontawesome-webfont.woff +0 -0
  32. data/app/assets/stylesheets/dash_creator/fonts/fontawesome-webfont.woff2 +0 -0
  33. data/app/assets/stylesheets/dash_creator/libs/font-awesome.css +2199 -0
  34. data/app/assets/stylesheets/dash_creator/libs/jquery.minicolors.css +319 -0
  35. data/app/assets/stylesheets/dash_creator/libs/jquery.minicolors.png +0 -0
  36. data/app/assets/stylesheets/dash_creator/libs/style.css +13714 -0
  37. data/app/assets/stylesheets/dash_creator/user.css +4 -0
  38. data/app/controllers/dash_creator/application_controller.rb +3 -0
  39. data/app/controllers/dash_creator/chart_controller.rb +81 -0
  40. data/app/controllers/dash_creator/dashboard_controller.rb +37 -0
  41. data/app/controllers/dash_creator/dashboard_object_controller.rb +34 -0
  42. data/app/controllers/dash_creator/filter_controller.rb +60 -0
  43. data/app/controllers/dash_creator/user_controller.rb +82 -0
  44. data/app/helpers/dash_creator/application_helper.rb +4 -0
  45. data/app/helpers/dash_creator/chart_helper.rb +829 -0
  46. data/app/helpers/dash_creator/dashboard_helper.rb +4 -0
  47. data/app/helpers/dash_creator/dashboard_object_helper.rb +4 -0
  48. data/app/helpers/dash_creator/filter_helper.rb +237 -0
  49. data/app/helpers/dash_creator/user_helper.rb +4 -0
  50. data/app/jobs/dash_creator/application_job.rb +4 -0
  51. data/app/mailers/dash_creator/application_mailer.rb +6 -0
  52. data/app/models/dash_creator/application_record.rb +5 -0
  53. data/app/models/dash_creator/chart.rb +21 -0
  54. data/app/models/dash_creator/dashboard.rb +13 -0
  55. data/app/models/dash_creator/dashboard_object.rb +7 -0
  56. data/app/models/dash_creator/filter.rb +22 -0
  57. data/app/views/dash_creator/chart/_chart_creator.html.erb +230 -0
  58. data/app/views/dash_creator/chart/_modals.html.erb +74 -0
  59. data/app/views/dash_creator/chart/_plot_chart.html.erb +45 -0
  60. data/app/views/dash_creator/chart/create_chart.js.erb +53 -0
  61. data/app/views/dash_creator/dashboard/_dashboard_creator.html.erb +182 -0
  62. data/app/views/dash_creator/dashboard/_modals.html.erb +74 -0
  63. data/app/views/dash_creator/dashboard_object/_chart.html.erb +9 -0
  64. data/app/views/dash_creator/dashboard_object/_stat.html.erb +24 -0
  65. data/app/views/dash_creator/dashboard_object/_table.html.erb +16 -0
  66. data/app/views/dash_creator/filter/_filtering_card.html.erb +132 -0
  67. data/app/views/dash_creator/filter/_modals.html.erb +51 -0
  68. data/app/views/dash_creator/filter/_result_tables.html.erb +55 -0
  69. data/app/views/dash_creator/filter/apply_filtering.js.erb +46 -0
  70. data/app/views/dash_creator/filter/create_stat.js.erb +20 -0
  71. data/app/views/dash_creator/layouts/application.html.erb +64 -0
  72. data/app/views/dash_creator/layouts/menu/_left_menu.html.erb +12 -0
  73. data/app/views/dash_creator/user/_section_card.html.erb +17 -0
  74. data/app/views/dash_creator/user/creator.html.erb +11 -0
  75. data/app/views/dash_creator/user/dashboard.html.erb +10 -0
  76. data/config/initializers/dash_creator.rb +0 -0
  77. data/config/routes.rb +30 -0
  78. data/lib/dash_creator.rb +87 -0
  79. data/lib/dash_creator/acts_as_dash_creator.rb +16 -0
  80. data/lib/dash_creator/acts_as_dashboard_object.rb +19 -0
  81. data/lib/dash_creator/engine.rb +11 -0
  82. data/lib/dash_creator/version.rb +3 -0
  83. data/lib/generators/dash_creator/install/install_generator.rb +70 -0
  84. data/lib/generators/dash_creator/install/templates/_chart.html.erb +9 -0
  85. data/lib/generators/dash_creator/install/templates/_section_card.html.erb +17 -0
  86. data/lib/generators/dash_creator/install/templates/_stat.html.erb +24 -0
  87. data/lib/generators/dash_creator/install/templates/_table.html.erb +16 -0
  88. data/lib/generators/dash_creator/install/templates/add_indexes_to_dash_creator_tables.rb +15 -0
  89. data/lib/generators/dash_creator/install/templates/create_dash_creator_charts.rb +25 -0
  90. data/lib/generators/dash_creator/install/templates/create_dash_creator_dashboard_objects.rb +36 -0
  91. data/lib/generators/dash_creator/install/templates/create_dash_creator_dashboards.rb +28 -0
  92. data/lib/generators/dash_creator/install/templates/create_dash_creator_filters.rb +22 -0
  93. data/lib/generators/dash_creator/install/templates/dashboard.html.erb +10 -0
  94. data/lib/generators/dash_creator/install/templates/initializer.rb +48 -0
  95. data/lib/tasks/dash_creator_tasks.rake +4 -0
  96. metadata +196 -0
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,3 @@
1
+ class DashCreator::ApplicationController < ::ApplicationController
2
+ protect_from_forgery with: :exception
3
+ end
@@ -0,0 +1,81 @@
1
+ require_dependency "dash_creator/application_controller"
2
+
3
+ module DashCreator
4
+ class ChartController < ApplicationController
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ def create_chart
8
+ return render json: {models: nil} if params[:filters].nil?
9
+
10
+ # Remove useless filters from data
11
+ chart_data = params
12
+ chart_data[:filters] = chart_data[:filters]
13
+ .to_unsafe_h
14
+ .select { |key, value| chart_data[:y_data].include?(key.to_s) || (key.to_s == chart_data[:x_data][:attribute].classify && chart_data[:x_data][:from_filter] == 'true' ) }
15
+
16
+ render 'dash_creator/chart/create_chart', locals: {chart_data: chart_data, filters_data: nil, id: 0}
17
+ end
18
+
19
+ def save_chart
20
+ return render json: {models: nil} if params[:filters].nil?
21
+
22
+ # Remove useless filters from data
23
+ chart_data = params
24
+ chart_data[:filters] = chart_data[:filters]
25
+ .to_unsafe_h
26
+ .select { |key, value| chart_data[:y_data].include?(key.to_s) || (key.to_s == chart_data[:x_data][:attribute].classify && chart_data[:x_data][:from_filter] == 'true' ) }
27
+
28
+ # Create chart from data
29
+ user = user_for_dash_creator
30
+ chart = DashCreator::Chart.all.where(user_id: user.id).create(name: params[:chart_name], data: chart_data)
31
+
32
+ render json: {chart_id: chart.id}
33
+ end
34
+
35
+ def edit_chart
36
+ return render json: {models: nil} if params[:filters].nil?
37
+
38
+ # Remove useless filters from data
39
+ chart_data = params
40
+ chart_data[:filters] = chart_data[:filters]
41
+ .to_unsafe_h
42
+ .select { |key, value| chart_data[:y_data].include?(key.to_s) || (key.to_s == chart_data[:x_data][:attribute].classify && chart_data[:x_data][:from_filter] == 'true' ) }
43
+
44
+ # Edit chart from data
45
+ user = user_for_dash_creator
46
+ chart = DashCreator::Chart.all.where(user_id: user.id).find(params[:chart_id])
47
+ chart.update_attribute(:data, chart_data)
48
+
49
+ render json: {chart_id: chart.id}
50
+ end
51
+
52
+ def get_chart
53
+ user = user_for_dash_creator
54
+ id = params[:chart_id]
55
+ chart = DashCreator::Chart.all.where(user_id: user.id).find(id)
56
+ refresh = params.key?('refresh') ? params['refresh'] : 'false'
57
+
58
+ filters_data = chart.data['filters']
59
+
60
+ chart_data = chart.data
61
+ chart_data['refresh'] = refresh
62
+ chart_data.delete('chart_id')
63
+ chart_data.delete('chart_name')
64
+
65
+ render 'dash_creator/chart/create_chart', locals: {chart_data: chart.data, filters_data: filters_data, id: id}
66
+ end
67
+
68
+ def delete_charts
69
+ user = user_for_dash_creator
70
+ DashCreator::Chart.all.where(user_id: user.id, id: params[:charts_ids]).destroy_all
71
+ end
72
+
73
+ def pluck_labels
74
+ model = params[:model].safe_constantize
75
+ attribute = params[:attribute]
76
+ limit = params[:limit].empty? ? 30 : params[:limit].to_i
77
+
78
+ render json: {labels: model.pluck(:"#{attribute}").uniq.take(limit)}
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,37 @@
1
+ require_dependency "dash_creator/application_controller"
2
+
3
+ module DashCreator
4
+ class DashboardController < ApplicationController
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ def get_dashboard
8
+ # user = user_for_dash_creator
9
+ # dashboard = DashCreator::Dashboard.all.where(user_id: user.id).find(params[:dashboard_id])
10
+ dashboard = DashCreator::Dashboard.all.find(params[:dashboard_id])
11
+
12
+ render json: dashboard.options
13
+ end
14
+
15
+ def save_dashboard
16
+ user = user_for_dash_creator
17
+ dashboard = DashCreator::Dashboard.all.where(user_id: user.id).create(name: params[:dashboard_name], options: params[:options])
18
+
19
+ render json: {dashboard_id: dashboard.id}
20
+ end
21
+
22
+ def edit_dashboard
23
+ # user = user_for_dash_creator
24
+ # dashboard = DashCreator::Dashboard.all.where(user_id: user.id).find(params[:dashboard_id])
25
+ dashboard = DashCreator::Dashboard.all.find(params[:dashboard_id])
26
+ dashboard.update_attribute(:options, params[:options])
27
+
28
+ render json: {dashboard_id: dashboard.id}
29
+ end
30
+
31
+ def delete_dashboards
32
+ # user = user_for_dash_creator
33
+ # DashCreator::Dashboard.all.where(user_id: user.id, id: params[:dashboards_ids]).destroy_all
34
+ DashCreator::Dashboard.all.where(id: params[:dashboards_ids]).destroy_all
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ require_dependency "dash_creator/application_controller"
2
+
3
+ module DashCreator
4
+ class DashboardObjectController < ApplicationController
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ def get_model_objects
8
+ object = DashboardObject.find_by_code(params['object_code'])
9
+
10
+ model_objects = object.related_model.classify.safe_constantize.all
11
+
12
+ model_objects_json = model_objects.map{ |o| {id: o.id, name: o.name} }
13
+
14
+ render json: {model_objects: model_objects_json}
15
+ end
16
+
17
+ def save_object
18
+ object = DashboardObject.create(name: params[:name], options: params[:options])
19
+
20
+ render json: {object_id: object.id}
21
+ end
22
+
23
+ def edit_object
24
+ object = DashboardObject.find_by_code(params['object_code'])
25
+ object.update_attribute(:options, params[:options])
26
+
27
+ render json: {object_id: object.id}
28
+ end
29
+
30
+ def delete_object
31
+ DashboardObject.find(params[:object_id]).destroy
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ require_dependency "dash_creator/application_controller"
2
+
3
+ module DashCreator
4
+ class FilterController < ApplicationController
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ def get_filter
8
+ user = user_for_dash_creator
9
+ filter = DashCreator::Filter.all.where(user_id: user.id).find(params[:filter_id])
10
+
11
+ render json: filter.options
12
+ end
13
+
14
+ def save_filter
15
+ user = user_for_dash_creator
16
+ filter = DashCreator::Filter.all.where(user_id: user.id).create(name: params[:filter_name], options: params[:filters])
17
+
18
+ render json: {filter_id: filter.id}
19
+ end
20
+
21
+ def delete_filters
22
+ user = user_for_dash_creator
23
+ DashCreator::Filter.all.where(user_id: user.id, id: params[:filters_ids]).destroy_all
24
+ end
25
+
26
+ def filtering_result
27
+ return render json: {models: nil} if params[:filters].nil?
28
+
29
+ numeric_types = Numeric.descendants.map{ |n| n.name.underscore }
30
+ @starting_types = numeric_types + %w(string date datetime)
31
+
32
+ render 'dash_creator/filter/apply_filtering', locals: {data: params['filters'], id: params['id']}
33
+ end
34
+
35
+ def download_csv
36
+ model = params[:model]
37
+ columns = params[:columns]
38
+ objects = model.safe_constantize.find(params[:ids])
39
+
40
+ file = CSV.generate do |csv|
41
+ csv << columns
42
+ objects.each do |o|
43
+ csv << o.attributes.values_at(*columns)
44
+ end
45
+ end
46
+
47
+ send_data file,
48
+ type: 'text/csv; charset=utf-8; header=present',
49
+ disposition: "attachment; filename=#{model}.csv"
50
+ end
51
+
52
+ def create_stat
53
+ id = params[:filter_id]
54
+ filters_data = DashCreator::Filter.find(id).options
55
+ filters_data['refresh'] = true unless params[:refresh].nil?
56
+
57
+ render 'dash_creator/filter/create_stat', locals: {filters_data: filters_data, id: id}
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,82 @@
1
+ require_dependency "dash_creator/application_controller"
2
+
3
+ module DashCreator
4
+ class UserController < ApplicationController
5
+ ##### Creator for filters, charts & dashboards #####
6
+
7
+ def creator
8
+ user = user_for_dash_creator
9
+
10
+ Rails.application.eager_load!
11
+ models = ApplicationRecord.descendants.select{ |m| !DashCreator.except_models.include?(m.name) }
12
+
13
+ @models = models.map{ |m| [m.name, m.name] }
14
+
15
+ @filters = DashCreator::Filter.all.where(user_id: user.id).all.map { |f| [f.name, f.id] }
16
+ @models_data = models_data
17
+ @charts = DashCreator::Chart.all.where(user_id: user.id).all.map { |c| [c.name, c.id] }
18
+ @dashboards = DashCreator::Dashboard.all.where(user_id: user.id).all.map { |d| [d.name, d.id] }
19
+
20
+ @dashboard_objects = DashboardObject.all
21
+ @model_objects = {}
22
+ @dashboard_objects.each do |o|
23
+ if o.related_model != ''
24
+ model = o.related_model.safe_constantize
25
+ model_objects = (model.column_names.include? 'user_id') ? model.where(user: user) : model.all
26
+ @model_objects[o.code] = model_objects.map{ |mo| {id: mo.id, name: mo.name} }
27
+ end
28
+ end
29
+
30
+ render :creator, layout: DashCreator.layout_path
31
+ end
32
+
33
+
34
+ ##### Render a dashboard #####
35
+
36
+ def dashboard
37
+ user = user_for_dash_creator
38
+ @dashboard = DashCreator::Dashboard.all.where(user_id: user.id).find(params[:dashboard_id])
39
+ @dashboards = DashCreator::Dashboard.all.where(user_id: user.id).map { |d| [d.name, d.id] }
40
+
41
+ render :dashboard, layout: DashCreator.layout_path
42
+ end
43
+
44
+
45
+ private
46
+ def models_data
47
+ Rails.application.eager_load!
48
+ models = ApplicationRecord.descendants.select{ |m| !DashCreator.except_models.include?(m.name.gsub('::', '')) }
49
+
50
+ numeric_types = Numeric.descendants.map{ |n| n.name.underscore }
51
+
52
+ models_data = {}
53
+ models.each do |m|
54
+ belongs_to_models = m.reflect_on_all_associations(:belongs_to)
55
+ .map(&:name)
56
+ .map{ |x| x.to_s << '_id' }
57
+ .map{ |x| DashCreator.columns_aliases[x].nil? ? x : DashCreator.columns_aliases[x] }
58
+
59
+ models_data[m.name] = []
60
+ m.columns_hash.each do |k,v|
61
+ if belongs_to_models.include?(k)
62
+ models_data[m.name].push(k + '-ref')
63
+ else
64
+ type = v.type.to_s
65
+ type = 'numeric' if numeric_types.include?(type)
66
+ type = 'text' if type == 'string'
67
+ type = 'datetime' if type == 'date'
68
+ models_data[m.name].push(k + '-' + type) unless DashCreator.except_attributes.include?(k)
69
+ end
70
+ end
71
+
72
+ has_many_models = m.reflect_on_all_associations(:has_many)
73
+ .map(&:name)
74
+ .map{ |x| x.to_s }
75
+ .select{ |k| !DashCreator.except_models.include?(k.classify) }
76
+ has_many_models.each { |h| models_data[m.name].push(h.singularize + '-has') }
77
+ end
78
+
79
+ models_data
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,4 @@
1
+ module DashCreator
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,829 @@
1
+ module DashCreator
2
+ module ChartHelper
3
+ def plot_chart(id, type, plot_data, options = {})
4
+ default_options = {
5
+ tooltips: {
6
+ mode: 'nearest',
7
+ intersect: false
8
+ },
9
+ responsive: true
10
+ }
11
+ options = options.merge(default_options)
12
+ render 'dash_creator/chart/plot_chart', {id: id, type: type, data: plot_data, options: options}
13
+ end
14
+
15
+ def create_update_button(id, type, text)
16
+ button_tag text, id: "btn-#{type}-#{id}"
17
+ end
18
+
19
+ def update_chart_script(id, type, action_path, action_params = {})
20
+ render 'dash_creator/chart/update_chart', {id: id, type: type, action_path: action_path, action_params: action_params}
21
+ end
22
+
23
+ # Data formatting (hash)
24
+ # :labels is array of labels
25
+ # :datasets_labels is array of datasets labels
26
+ # :datasets is array of arrays containing the datasets data
27
+ # for a mixed chart, :types is array of datasets types
28
+ # :style contains chart styling
29
+ def plot_type_1_chart(id, type, data, options = {})
30
+ begin
31
+ raise ArgumentError, 'datasets_key_missing' unless data.key?('datasets')
32
+ raise ArgumentError, 'labels_key_missing' unless data.key?('labels')
33
+ raise ArgumentError, 'types_key_missing' if (type == 'mixed' && !data.key?('types'))
34
+
35
+ plot_data = type_1_data_to_plot(data)
36
+ rescue Exception => e
37
+ return render html: create_plot_error_message(id, type, e.message).to_s
38
+ end
39
+ plot_data_with_options = type_1_add_style(plot_data, data['style'])
40
+ plot_data_with_options[:options] = plot_data_with_options[:options].merge(options)
41
+
42
+ plot_chart(id, type, plot_data_with_options[:plot_data], plot_data_with_options[:options])
43
+ end
44
+
45
+ def type_1_data_to_plot(data)
46
+ datasets = []
47
+ 0.upto(data['datasets'].length-1) do |i|
48
+ set = {data: data['datasets'][i]}
49
+ unless data['labels'][i].nil?
50
+ set[:label] = data['datasets_labels'][i] unless data['datasets_labels'].nil?
51
+ end
52
+ set[:type] = data['types'][i]
53
+ datasets.push(set)
54
+ end
55
+
56
+ {labels: data['labels'], datasets: datasets}
57
+ end
58
+
59
+ def type_1_add_style(plot_data, style_options)
60
+ colors = style_options['colors'].nil? ? nil : set_transparency(style_options['colors'], style_options['transparencies'])
61
+
62
+ 0.upto(plot_data[:datasets].length-1) do |i|
63
+ set = plot_data[:datasets][i]
64
+ set[:backgroundColor] = colors[i] unless colors.nil? || colors[i].nil?
65
+ set[:borderColor] = style_options['colors'][i] unless style_options['colors'].nil? || style_options['colors'][i].nil?
66
+ end
67
+
68
+ # Initialize options
69
+ options = {}
70
+ scales = options['scales'] = {}
71
+ scales['xAxes'] = [{}]
72
+ scales['yAxes'] = [{}]
73
+ scales['xAxes'][0]['gridLines'] = {}
74
+ scales['xAxes'][0]['ticks'] = {}
75
+ scales['yAxes'][0]['gridLines'] = {}
76
+ scales['yAxes'][0]['ticks'] = {}
77
+
78
+ # Grid options
79
+ grid_options = style_options['grid']
80
+ scales['xAxes'][0]['gridLines']['display'] = false if grid_options['x'] == 'false'
81
+ scales['yAxes'][0]['gridLines']['display'] = false if grid_options['y'] == 'false'
82
+
83
+ # Y Axis options
84
+ y_axis_options = style_options['y-axis']
85
+ scales['yAxes'][0]['ticks']['min'] = y_axis_options['min'].to_i if y_axis_options['min'] != ''
86
+ scales['yAxes'][0]['ticks']['max'] = y_axis_options['max'].to_i if y_axis_options['max'] != ''
87
+ scales['yAxes'][0]['ticks']['stepSize'] = y_axis_options['step'].to_i if y_axis_options['step'] != ''
88
+
89
+ # Stacked
90
+ if style_options['stacked'] == 'true'
91
+ scales['xAxes'][0]['stacked'] = true
92
+ scales['yAxes'][0]['stacked'] = true
93
+ end
94
+
95
+ # Labeling step
96
+ scales['xAxes'][0]['ticks']['callback'] = style_options['labeling-step'] if style_options['labeling-step'] != ''
97
+
98
+ # Legend
99
+ legend_options = style_options['legend']
100
+ legend = options['legend'] = {}
101
+ legend['display'] = false if legend_options['display'] == 'false'
102
+ legend['position'] = legend_options['position']
103
+
104
+ # Title
105
+ options['title'] = {display: true, text: style_options['title']} if style_options['title'] != ''
106
+
107
+ {plot_data: plot_data, options: options}
108
+ end
109
+
110
+
111
+
112
+ # Data formatting (hash)
113
+ # :labels is array of labels
114
+ # :datasets is array of arrays containing the datasets data
115
+ # :style contains chart styling
116
+ def plot_type_2_chart(id, type, data, options = {})
117
+ begin
118
+ raise ArgumentError, 'datasets_key_missing' unless data.key?('datasets')
119
+ raise ArgumentError, 'labels_key_missing' unless data.key?('labels')
120
+
121
+ plot_data = type_2_data_to_plot(data)
122
+ rescue Exception => e
123
+ return render html: create_plot_error_message(id, type, e.message).to_s
124
+ end
125
+ plot_data_with_options = type_2_add_style(plot_data, data['style'])
126
+ plot_data_with_options = plot_data_with_options.merge(options)
127
+
128
+ plot_chart(id, type, plot_data_with_options[:plot_data], plot_data_with_options[:options])
129
+ end
130
+
131
+ def type_2_data_to_plot(data)
132
+ datasets = []
133
+ 0.upto(data['datasets'].length-1) do |i|
134
+ set = {
135
+ data: data['datasets'][i]
136
+ }
137
+ datasets.push(set)
138
+ end
139
+
140
+ {labels: data['labels'], datasets: datasets}
141
+ end
142
+
143
+ def type_2_add_style(plot_data, style_options)
144
+ 0.upto(plot_data[:datasets].length-1) do |i|
145
+ set = plot_data[:datasets][i]
146
+ set[:backgroundColor] = set_transparency(style_options['colors'], style_options['transparencies']) unless style_options['colors'].nil?
147
+ end
148
+
149
+ options = {}
150
+
151
+ legend_options = style_options['legend']
152
+ legend = options['legend'] = {}
153
+ legend['display'] = false if legend_options['display'] == 'false'
154
+ legend['position'] = legend_options['position']
155
+
156
+ options['title'] = {display: true, text: style_options['title']} if style_options['title'] != ''
157
+
158
+ {plot_data: plot_data, options: options}
159
+ end
160
+
161
+
162
+ # Type 1 charts & mixed bar/line
163
+ def plot_line_chart(id, data, options = {})
164
+ plot_type_1_chart(id, 'line', data, options)
165
+ end
166
+
167
+ def plot_bar_chart(id, data, options = {})
168
+ plot_type_1_chart(id, 'bar', data, options)
169
+ end
170
+
171
+ def plot_mixed_chart(id, data, options = {})
172
+ plot_type_1_chart(id, 'mixed', data, options)
173
+ end
174
+
175
+
176
+ # Type 2 charts
177
+ def plot_pie_chart(id, data, options = {})
178
+ plot_type_2_chart(id, 'pie', data, options)
179
+ end
180
+
181
+ def plot_doughnut_chart(id, data, options = {})
182
+ plot_type_2_chart(id, 'doughnut', data, options)
183
+ end
184
+
185
+ def plot_polar_chart(id, data, options = {})
186
+ plot_type_2_chart(id, 'polarArea', data, options)
187
+ end
188
+
189
+ def plot_radar_chart(id, data, options = {})
190
+ plot_type_2_chart(id, 'radar', data, options)
191
+ end
192
+
193
+
194
+ # From rgb to rgba with given colors and transparencies
195
+ def set_transparency(colors, transparencies)
196
+ colors_with_transparency = []
197
+ 0.upto(colors.length - 1).each { |i| colors_with_transparency.push('rgba' + colors[i][3..-2] + ', ' + (1-(transparencies[i].to_f/100)).to_s + ')') }
198
+ colors_with_transparency
199
+ end
200
+
201
+ # Returns plot failing error message
202
+ def create_plot_error_message(id, type, error_key)
203
+ str = error_key + " for chart-#{type}-#{id}"
204
+ str
205
+ end
206
+
207
+
208
+ # Data processing for charts
209
+ # Handle differently regarding x_data type
210
+ def chart_processing(data)
211
+ ActiveRecord::Base.connection.execute("SET temp_buffers = '4GB';")
212
+ case data['x_data']['type']
213
+ when 'datetime'
214
+ hash = date_chart_processing(data)
215
+
216
+ when 'numeric'
217
+ hash = numeric_chart_processing(data)
218
+
219
+ when 'boolean'
220
+ hash = boolean_chart_processing(data)
221
+
222
+ when 'text'
223
+ hash = text_chart_processing(data)
224
+
225
+ when 'has'
226
+ hash = has_model_chart_processing(data)
227
+
228
+ when 'ref'
229
+ hash = ref_model_chart_processing(data)
230
+
231
+ else
232
+ hash = {}
233
+ end
234
+ ActiveRecord::Base.connection.execute("SET temp_buffers = '8MB';")
235
+ hash = hash.merge({
236
+ datasets_labels: data['datasets_labels'],
237
+ types: data['types']
238
+ })
239
+ hash
240
+ end
241
+
242
+ def get_start_and_end_dates(date_info)
243
+ date_range_type = date_info['date_range_type']
244
+
245
+ case date_range_type
246
+ when 'Today'
247
+ start_date = DateTime.now.beginning_of_day
248
+ end_date = DateTime.now.beginning_of_day + 1.days
249
+
250
+ when 'Yesterday'
251
+ start_date = DateTime.now.beginning_of_day - 1.days
252
+ end_date = DateTime.now.beginning_of_day
253
+
254
+ when 'Last 7 Days'
255
+ start_date = DateTime.now.beginning_of_day - 6.days
256
+ end_date = DateTime.now.beginning_of_day + 1.days
257
+
258
+ when 'This Week'
259
+ start_date = DateTime.now.beginning_of_week
260
+ end_date = DateTime.now.beginning_of_week + 7.days
261
+
262
+ when 'Last Week'
263
+ start_date = DateTime.now.beginning_of_week - 7.days
264
+ end_date = DateTime.now.beginning_of_week
265
+
266
+ when 'Last 30 Days'
267
+ start_date = DateTime.now.beginning_of_day - 29.days
268
+ end_date = DateTime.now.beginning_of_day + 1.days
269
+
270
+ when 'This Month'
271
+ start_date = DateTime.now.beginning_of_month
272
+ end_date = DateTime.now.beginning_of_month + 30.days
273
+
274
+ when 'Last Month'
275
+ start_date = DateTime.now.beginning_of_month - 30.days
276
+ end_date = DateTime.now.beginning_of_month
277
+
278
+ when 'Last 365 Days'
279
+ start_date = DateTime.now.beginning_of_day - 364.days
280
+ end_date = DateTime.now.beginning_of_day + 1.days
281
+
282
+ when 'This Year'
283
+ start_date = DateTime.now.beginning_of_year
284
+ end_date = DateTime.now.beginning_of_year + 365.days
285
+
286
+ when 'Last Year'
287
+ start_date = DateTime.now.beginning_of_year - 365.days
288
+ end_date = DateTime.now.beginning_of_year
289
+
290
+ else
291
+ start_date = DateTime.strptime(date_info['start'], '%b %d, %Y').beginning_of_day
292
+ end_date = DateTime.strptime(date_info['end'], '%b %d, %Y').beginning_of_day + 1.days
293
+ end
294
+
295
+ [start_date, end_date]
296
+ end
297
+
298
+ # Process chart data for a x timeline
299
+ def date_chart_processing(data)
300
+ date_info = data['x_data']
301
+ y_data = data['y_data']
302
+
303
+ sub_attribute = date_info['sub_attribute']
304
+ unless sub_attribute.nil?
305
+ return sub_has_date_chart_processing(data) if date_info['sub_attribute_from'] == 'has'
306
+ return sub_ref_date_chart_processing(data)
307
+ end
308
+
309
+ # Gather all info
310
+ attribute = date_info['attribute']
311
+ format = date_info.key?('format') ? date_info['format'] : '%d.%m.%Y'
312
+ end_date = get_start_and_end_dates(date_info)[1]
313
+ labels = get_date_labels(date_info)
314
+
315
+ datasets = []
316
+ 0.upto(y_data.length-1).each {datasets.push([])}
317
+ 0.upto(labels.length-1).each do |i|
318
+ date = labels[i]
319
+ next_date = i == (labels.length - 1) ? end_date : labels[i+1]
320
+ 0.upto(y_data.length-1).each do |k|
321
+ count = y_data[k].where("#{attribute}": date..next_date).count
322
+ datasets[k].push(count)
323
+ end
324
+ end
325
+
326
+ labels = labels.map{ |d| d.strftime(format) }
327
+
328
+ {labels: labels, datasets: datasets}
329
+ end
330
+
331
+ def sub_has_date_chart_processing(data)
332
+ date_info = data['x_data']
333
+ y_data = data['y_data']
334
+
335
+ # Gather all info
336
+ # has_models = date_info['attribute'].pluralize
337
+ model_string = y_data.first.first.class.name.underscore
338
+ attribute_model = date_info['attribute'].classify.safe_constantize
339
+ sub_attribute = date_info['sub_attribute']
340
+
341
+ format = date_info.key?('format') ? date_info['format'] : '%d.%m.%Y'
342
+ end_date = get_start_and_end_dates(date_info)[1]
343
+ labels = get_date_labels(date_info)
344
+
345
+ datasets = []
346
+ model_ids = []
347
+ 0.upto(y_data.length-1).each do |i|
348
+ datasets.push([])
349
+ model_ids.push(y_data[i].pluck(:id).uniq)
350
+ end
351
+
352
+ 0.upto(labels.length-1).each do |i|
353
+ date = labels[i]
354
+ next_date = i == (labels.length - 1) ? end_date : labels[i+1]
355
+ 0.upto(y_data.length-1).each do |k|
356
+ # data = y_data[k].joins("LEFT JOIN #{has_models} ON #{has_models}.#{model_string}_id = #{model_string.pluralize}.id")
357
+ # .group("#{model_string.pluralize}.id")
358
+ # .having("#{has_models}.#{sub_attribute}": date..next_date)
359
+
360
+ # total_count = 0
361
+ # data.count.each { |key, count| total_count += count }
362
+ # datasets[k].push(total_count)
363
+
364
+ filtered_model_ids = attribute_model.where("#{sub_attribute}": date..next_date).pluck("#{model_string}_id")
365
+
366
+ datasets[i].push(count_values_in_common(model_ids[i], filtered_model_ids))
367
+ end
368
+ end
369
+
370
+ labels = labels.map { |d| d.strftime(format) }
371
+
372
+ {labels: labels, datasets: datasets}
373
+ end
374
+
375
+ def sub_ref_date_chart_processing(data)
376
+ date_info = data['x_data']
377
+ y_data = data['y_data']
378
+
379
+ # Gather all info
380
+ ref_model = date_info['attribute'][0..-4]
381
+ sub_attribute = date_info['sub_attribute']
382
+
383
+ format = date_info.key?('format') ? date_info['format'] : '%d.%m.%Y'
384
+ end_date = get_start_and_end_dates(date_info)[1]
385
+ labels = get_date_labels(date_info)
386
+
387
+ datasets = []
388
+ 0.upto(y_data.length-1).each {datasets.push([])}
389
+ 0.upto(labels.length-1).each do |i|
390
+ date = labels[i]
391
+ next_date = i == (labels.length - 1) ? end_date : labels[i+1]
392
+ 0.upto(y_data.length-1).each do |k|
393
+ count = y_data[k].joins(:"#{ref_model}")
394
+ .where("#{ref_model.pluralize}.#{sub_attribute}": date..next_date)
395
+ .count
396
+
397
+ datasets[k].push(count)
398
+ end
399
+ end
400
+
401
+ labels = labels.map { |d| d.strftime(format) }
402
+
403
+ {labels: labels, datasets: datasets}
404
+ end
405
+
406
+ def numeric_chart_processing(data)
407
+ x_data = data['x_data']
408
+ y_data = data['y_data']
409
+
410
+ attribute = x_data['attribute']
411
+
412
+ sub_attribute = x_data['sub_attribute']
413
+ unless sub_attribute.nil?
414
+ return sub_has_numeric_chart_processing(data) if x_data['sub_attribute_from'] == 'has'
415
+ return sub_ref_numeric_chart_processing(data)
416
+ end
417
+
418
+ datasets = []
419
+ 0.upto(y_data.length-1).each {datasets.push([])}
420
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
421
+
422
+ x_data['labels'].each do |l|
423
+ 0.upto(y_data.length-1).each do |i|
424
+ split = l.split('-')
425
+ low = l[-1] == '+' ? l[0..-2] : split[0]
426
+ high = split.length == 2 ? split[1] : low
427
+ data = y_data[i].where("#{attribute}" + ' >= ?', low)
428
+ data = data.where("#{attribute}" + ' <= ?', high) unless l[-1] == '+'
429
+
430
+ datasets[i].push(data.count)
431
+ end
432
+ end
433
+
434
+ {labels: labels, datasets: datasets}
435
+ end
436
+
437
+ def sub_has_numeric_chart_processing(data)
438
+ x_data = data['x_data']
439
+ y_data = data['y_data']
440
+
441
+ model_string = y_data.first.first.class.name.underscore
442
+ attribute_model = x_data['attribute'].classify.safe_constantize
443
+ sub_attribute = x_data['sub_attribute']
444
+
445
+ datasets = []
446
+ model_ids = []
447
+ 0.upto(y_data.length-1).each do |i|
448
+ datasets.push([])
449
+ model_ids.push(y_data[i].pluck(:id).uniq)
450
+ end
451
+
452
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
453
+
454
+ x_data['labels'].each do |l|
455
+ 0.upto(y_data.length-1).each do |i|
456
+ split = l.split('-')
457
+ low = l[-1] == '+' ? l[0..-2] : split[0]
458
+ high = split.length == 2 ? split[1] : low
459
+
460
+ if l[-1] == '+'
461
+ filtered_model_ids = attribute_model.where("#{sub_attribute} >= ?", low).pluck("#{model_string}_id")
462
+ else
463
+ filtered_model_ids = attribute_model.where("#{sub_attribute}": low..high).pluck("#{model_string}_id")
464
+ end
465
+
466
+ datasets[i].push(count_values_in_common(model_ids[i], filtered_model_ids))
467
+ end
468
+ end
469
+
470
+ {labels: labels, datasets: datasets}
471
+ end
472
+
473
+ def sub_ref_numeric_chart_processing(data)
474
+ x_data = data['x_data']
475
+ y_data = data['y_data']
476
+
477
+ ref_model = x_data['attribute'][0..-4]
478
+ sub_attribute = x_data['sub_attribute']
479
+
480
+ datasets = []
481
+ 0.upto(y_data.length-1).each {datasets.push([])}
482
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
483
+
484
+ x_data['labels'].each do |l|
485
+ 0.upto(y_data.length-1).each do |i|
486
+ split = l.split('-')
487
+ low = l[-1] == '+' ? l[0..-2] : split[0]
488
+ high = split.length == 2 ? split[1] : low
489
+
490
+ if l[-1] == '+'
491
+ data = y_data[i].joins(:"#{ref_model}").where("#{ref_model.pluralize}.#{sub_attribute} >= ?", low)
492
+ else
493
+ data = y_data[i].joins(:"#{ref_model}").where("#{ref_model.pluralize}.#{sub_attribute}": low..high)
494
+ end
495
+
496
+ datasets[i].push(data.count)
497
+ end
498
+ end
499
+
500
+ {labels: labels, datasets: datasets}
501
+ end
502
+
503
+ def boolean_chart_processing(data)
504
+ x_data = data['x_data']
505
+ y_data = data['y_data']
506
+
507
+ attribute = x_data['attribute']
508
+
509
+ sub_attribute = x_data['sub_attribute']
510
+ unless sub_attribute.nil?
511
+ return sub_has_boolean_chart_processing(data) if x_data['sub_attribute_from'] == 'has'
512
+ return sub_ref_boolean_chart_processing(data)
513
+ end
514
+
515
+ datasets = []
516
+ 0.upto(y_data.length-1).each {datasets.push([])}
517
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
518
+
519
+ x_data['labels'].each do |l|
520
+ 0.upto(y_data.length-1).each do |i|
521
+ data = y_data[i].where("#{attribute}": true_string?(l))
522
+
523
+ datasets[i].push(data.count)
524
+ end
525
+ end
526
+
527
+ {labels: labels, datasets: datasets}
528
+ end
529
+
530
+ def sub_has_boolean_chart_processing(data)
531
+ x_data = data['x_data']
532
+ y_data = data['y_data']
533
+
534
+ model_string = y_data.first.first.class.name.underscore
535
+ attribute_model = x_data['attribute'].classify.safe_constantize
536
+ sub_attribute = x_data['sub_attribute']
537
+
538
+ datasets = []
539
+ model_ids = []
540
+ 0.upto(y_data.length-1).each do |i|
541
+ datasets.push([])
542
+ model_ids.push(y_data[i].pluck(:id).uniq)
543
+ end
544
+
545
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
546
+
547
+ x_data['labels'].each do |l|
548
+ 0.upto(y_data.length-1).each do |i|
549
+ filtered_model_ids = attribute_model.where("#{sub_attribute}": true_string?(l)).pluck("#{model_string}_id")
550
+
551
+ datasets[i].push(count_values_in_common(model_ids[i], filtered_model_ids))
552
+ end
553
+ end
554
+
555
+ {labels: labels, datasets: datasets}
556
+ end
557
+
558
+ def sub_ref_boolean_chart_processing(data)
559
+ x_data = data['x_data']
560
+ y_data = data['y_data']
561
+
562
+ ref_model = x_data['attribute'][0..-4]
563
+ sub_attribute = x_data['sub_attribute']
564
+
565
+ datasets = []
566
+ 0.upto(y_data.length-1).each {datasets.push([])}
567
+ labels = x_data['labels-corres'].blank? ? x_data['labels'] : x_data['labels-corres']
568
+
569
+ x_data['labels'].each do |l|
570
+ 0.upto(y_data.length-1).each do |i|
571
+ data = y_data[i].joins(:"#{ref_model}").where("#{ref_model.pluralize}.#{sub_attribute}": true_string?(l))
572
+
573
+ datasets[i].push(data.count)
574
+ end
575
+ end
576
+
577
+ {labels: labels, datasets: datasets}
578
+ end
579
+
580
+ def text_chart_processing(data)
581
+ x_data = data['x_data']
582
+ y_data = data['y_data']
583
+
584
+ sub_attribute = x_data['sub_attribute']
585
+ unless sub_attribute.nil?
586
+ return sub_has_text_chart_processing(data) if x_data['sub_attribute_from'] == 'has'
587
+ return sub_ref_text_chart_processing(data)
588
+ end
589
+
590
+ attribute = x_data['attribute']
591
+
592
+ datasets = []
593
+ 0.upto(y_data.length-1).each {datasets.push([])}
594
+
595
+ labels = get_or_pluck_labels(x_data, y_data)
596
+
597
+ labels.each do |l|
598
+ 0.upto(y_data.length-1).each do |i|
599
+ datasets[i].push(y_data[i].where("#{attribute}": l).count)
600
+ end
601
+ end
602
+
603
+ {labels: labels, datasets: datasets}
604
+ end
605
+
606
+ def sub_has_text_chart_processing(data)
607
+ x_data = data['x_data']
608
+ y_data = data['y_data']
609
+
610
+ model_string = y_data.first.first.class.name.underscore
611
+ attribute_model = x_data['attribute'].classify.safe_constantize
612
+ sub_attribute = x_data['sub_attribute']
613
+
614
+ datasets = []
615
+ model_ids = []
616
+ 0.upto(y_data.length-1).each do |i|
617
+ datasets.push([])
618
+ model_ids.push(y_data[i].pluck(:id).uniq)
619
+ end
620
+
621
+ labels = get_or_pluck_labels(x_data, y_data)
622
+
623
+ labels.each do |l|
624
+ 0.upto(y_data.length-1).each do |i|
625
+ filtered_model_ids = attribute_model.where("#{sub_attribute}": l).pluck("#{model_string}_id")
626
+
627
+ datasets[i].push(count_values_in_common(model_ids[i], filtered_model_ids))
628
+ end
629
+ end
630
+
631
+ {labels: labels, datasets: datasets}
632
+ end
633
+
634
+ def sub_ref_text_chart_processing(data)
635
+ x_data = data['x_data']
636
+ y_data = data['y_data']
637
+
638
+ ref_model = x_data['attribute'][0..-4]
639
+ sub_attribute = x_data['sub_attribute']
640
+
641
+ datasets = []
642
+ 0.upto(y_data.length-1).each {datasets.push([])}
643
+
644
+ labels = get_or_pluck_labels(x_data, y_data)
645
+
646
+ labels.each do |l|
647
+ 0.upto(y_data.length-1).each do |i|
648
+ datasets[i].push(y_data[i].joins(:"#{ref_model}").where("#{ref_model.pluralize}.#{sub_attribute} = '#{l}'").count)
649
+ end
650
+ end
651
+
652
+ {labels: labels, datasets: datasets}
653
+ end
654
+
655
+ # Has is necessarily a count : if a sub-attribute is given, then label_type becomes the one of the sub attribute and cannot be has
656
+ def has_model_chart_processing(data)
657
+ x_data = data['x_data']
658
+ y_data = data['y_data']
659
+
660
+ has_models = x_data['attribute'].pluralize
661
+ model_string = y_data.first.first.class.name.underscore
662
+ # attribute_model = x_data['attribute'].classify.safe_constantize
663
+
664
+ datasets = []
665
+ # model_ids = []
666
+ 0.upto(y_data.length-1).each do |i|
667
+ datasets.push([])
668
+ # model_ids.push(y_data[i].pluck(:id).uniq)
669
+ end
670
+ # filtered_model_ids = attribute_model.pluck("#{model_string}_id")
671
+ # count_hash = count_hash(filtered_model_ids)
672
+
673
+ labels = x_data['labels']
674
+ labels.each do |l|
675
+ 0.upto(y_data.length-1).each do |i|
676
+ split = l.split('-')
677
+ low = l[-1] == '+' ? l[0..-2] : split[0]
678
+ high = split.length == 2 ? split[1] : low
679
+
680
+ if l[-1] == '+'
681
+ data = y_data[i].joins("LEFT JOIN #{has_models} ON #{has_models}.#{model_string}_id = #{model_string.pluralize}.id")
682
+ .group("#{model_string.pluralize}.id")
683
+ .having("count(#{has_models}.id) >= ?", low)
684
+ # count = count_hash.select{ |k, v| v >= low<to_i }.count
685
+ else
686
+ data = y_data[i].joins("LEFT JOIN #{has_models} ON #{has_models}.#{model_string}_id = #{model_string.pluralize}.id")
687
+ .group("#{model_string.pluralize}.id")
688
+ .having("count(#{has_models}.id) BETWEEN ? AND ?", low, high)
689
+ # count = count_hash.select{ |k, v| v >= low.to_i && v <= high.to_i }.count
690
+ end
691
+
692
+ # datasets[i].push(count)
693
+ sum = 0
694
+ data.count.each { |key, count| sum += count }
695
+ datasets[i].push(sum)
696
+ end
697
+ end
698
+
699
+ {labels: labels, datasets: datasets}
700
+ end
701
+
702
+ def ref_model_chart_processing(data)
703
+ x_data = data['x_data']
704
+ y_data = data['y_data']
705
+
706
+ ref_model = x_data['attribute']
707
+
708
+ datasets = []
709
+ 0.upto(y_data.length-1).each {datasets.push([])}
710
+
711
+ labels = ['Having', 'Not having']
712
+ 0.upto(y_data.length-1).each do |i|
713
+ tot_count = y_data[i].count
714
+ having_count = y_data[i].where.not("#{ref_model}": nil).count
715
+ datasets[0].push(having_count).push(tot_count - having_count)
716
+ end
717
+
718
+ {labels: labels, datasets: datasets}
719
+ end
720
+
721
+
722
+ # Returns the date group size regarding date_info
723
+ def date_label_group_size(date_info)
724
+ case date_info['period']
725
+ when 'day'
726
+ 1
727
+ when 'week'
728
+ 7
729
+ else
730
+ 30
731
+ end
732
+ end
733
+
734
+ def get_date_labels(date_info)
735
+ limit_dates = get_start_and_end_dates(date_info)
736
+ start_date = limit_dates[0]
737
+ end_date = limit_dates[1]
738
+ period_length = (end_date - start_date).to_i
739
+ group_size = date_label_group_size(date_info)
740
+
741
+ labels = []
742
+ (0..(period_length-1)).step(group_size).each do |i|
743
+ date = (start_date + i.days)
744
+ labels.push(date)
745
+ end
746
+ labels
747
+ end
748
+
749
+ def get_or_pluck_labels(x_data, y_data)
750
+ case x_data['label_type']
751
+ when 'text-auto'
752
+ sub_attribute = x_data['sub_attribute']
753
+ pluck_from = sub_attribute.nil? ? y_data.first : x_data['attribute'][0..-4].classify.safe_constantize
754
+ to_pluck = sub_attribute.nil? ? x_data['attribute'] : sub_attribute
755
+
756
+ labels = pluck_from.pluck(:"#{to_pluck}").uniq
757
+
758
+ when 'text'
759
+ labels = x_data['labels']
760
+ end
761
+ labels
762
+ end
763
+
764
+ def count_values_in_common(model_ids, filtered_model_ids)
765
+ values_in_common = (model_ids & filtered_model_ids)
766
+
767
+ count_hash = count_hash(filtered_model_ids)
768
+
769
+ count = 0
770
+ values_in_common.each { |v| count += count_hash[v] }
771
+
772
+ count
773
+ end
774
+
775
+ def count_hash(array)
776
+ array.group_by(&:itself).map { |k,v| [k, v.count] }.to_h
777
+ end
778
+
779
+ include FilterHelper
780
+ def chart_processed_data_from_redis(chart_data)
781
+ redis_data = nil
782
+ style = chart_data.delete('style')
783
+ redis_chart_data = encode_chart_data(chart_data)
784
+
785
+ unless chart_data['refresh'] == 'true' || DashCreator.redis_store_variable.nil?
786
+ redis_data = DashCreator.redis_store_variable.get(redis_chart_data)
787
+ end
788
+
789
+ unless redis_data.nil?
790
+ processed_data = JSON.parse(redis_data)
791
+ else
792
+ chart_data_with_records = prepare_data(chart_data)
793
+ processed_data = chart_processing(chart_data_with_records).deep_stringify_keys
794
+ processed_data['last_updated'] = DateTime.now.strftime('%d/%m/%Y - %T')
795
+
796
+ # Add chart data to redis
797
+ DashCreator.redis_store_variable.set(redis_chart_data, processed_data.to_json) unless DashCreator.redis_store_variable.nil?
798
+ end
799
+
800
+ processed_data['style'] = style
801
+ processed_data['style'] = processed_data['style'].to_unsafe_h unless processed_data['style'].is_a?(Hash)
802
+
803
+ processed_data
804
+ end
805
+
806
+ def encode_chart_data(chart_data)
807
+ # Prepare data to encode
808
+ encoded_chart_data = chart_data.clone
809
+ encoded_chart_data = encoded_chart_data.to_unsafe_h unless encoded_chart_data.is_a?(Hash)
810
+ encoded_chart_data.delete('controller')
811
+ encoded_chart_data.delete('action')
812
+ encoded_chart_data.delete('refresh')
813
+
814
+ # Encode
815
+ hash_hash(encoded_chart_data)
816
+ end
817
+
818
+ # This helper function is used in create_chart.js.erb to hash param hash to store it in Redis
819
+ def hash_hash(h)
820
+ require 'digest/sha1'
821
+ str = recursive_flatten(h).sort.join
822
+ Digest::SHA1.hexdigest(str)
823
+ end
824
+
825
+ def recursive_flatten(h)
826
+ h.flatten(-1).map{|el| el.is_a?(Hash) ? recursive_flatten(el) : el}.flatten
827
+ end
828
+ end
829
+ end