fios 0.1.0 → 1.0.1
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/Gemfile.lock +1 -1
- data/fios.gemspec +2 -2
- data/lib/fios/adapters/active_record/chart_query.rb +106 -0
- data/lib/fios/adapters/active_record/report_query.rb +88 -0
- data/lib/fios/adapters/active_record_adapter.rb +24 -7
- data/lib/fios/adapters/base.rb +7 -5
- data/lib/fios/builders/chart_builder.rb +12 -117
- data/lib/fios/builders/report_builder.rb +12 -95
- data/lib/fios/services/dataset_fetcher.rb +10 -5
- data/lib/fios/version.rb +1 -1
- data/lib/fios.rb +2 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c65af31bd0d116984a3feb0410ce53348171b425bf5e0d07dd45f00a49a8bf5b
|
|
4
|
+
data.tar.gz: b3b3292ba5c877af0bdf3d39258a7d5c2c2f177efe5e6a657ef1ee6c72dc44d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90059e0ae8e22cb45bc602c267381501c7786d7ce77520c2ea3faf639c9d496efea1f7ef673f34cfb3b55a656ba730171a2bd8d09258af153347e4ecb62f81de
|
|
7
|
+
data.tar.gz: 6dd0c0e6aa80bcd9baec5b1ae987ded80477d4aa9dcb654dfcd04c56a74fad68adfb5f9d0dc92953f881ac8952260d9bf322defa3bd3a2fc060d85dac61f448d
|
data/Gemfile.lock
CHANGED
data/fios.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ["Mark Harbison"]
|
|
9
9
|
spec.email = ["mark@tyne-solutions.com"]
|
|
10
10
|
|
|
11
|
-
spec.summary = "
|
|
12
|
-
spec.description = "
|
|
11
|
+
spec.summary = "A data analytics framework for building charts, dashboards, and reports in Ruby."
|
|
12
|
+
spec.description = "A data analytics framework for building charts, dashboards, and reports in Ruby."
|
|
13
13
|
spec.homepage = "https://github.com/CodeTectonics/fios"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
spec.required_ruby_version = ">= 3.2.0"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module Fios
|
|
2
|
+
module Adapters
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class ChartQuery
|
|
5
|
+
def self.add_select_clause(query, chart_config)
|
|
6
|
+
fields = []
|
|
7
|
+
fields << chart_config['x_axis']['attr']
|
|
8
|
+
|
|
9
|
+
chart_config['y_axes'].each do |column|
|
|
10
|
+
aggregate = aggregated_attr(column['attr'], column['aggregation'])
|
|
11
|
+
fields << "#{aggregate[0]} AS '#{aggregate[1]}'"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
query.select(fields)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.aggregated_attr(attr, aggregation)
|
|
18
|
+
case aggregation
|
|
19
|
+
when 'count'
|
|
20
|
+
["COUNT(#{attr})", "num_#{attr}"]
|
|
21
|
+
when 'count_distinct'
|
|
22
|
+
["COUNT(DISTINCT #{attr})", "num_uniq_#{attr}"]
|
|
23
|
+
when 'average'
|
|
24
|
+
["AVG(#{attr})", "avg_#{attr}"]
|
|
25
|
+
when 'min'
|
|
26
|
+
["MIN(#{attr})", "min_#{attr}"]
|
|
27
|
+
when 'max'
|
|
28
|
+
["MAX(#{attr})", "max_#{attr}"]
|
|
29
|
+
when 'sum'
|
|
30
|
+
["SUM(#{attr})", "sum_#{attr}"]
|
|
31
|
+
else
|
|
32
|
+
attr
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.add_where_clause(query, chart_config)
|
|
37
|
+
return query if chart_config['filters'].blank?
|
|
38
|
+
|
|
39
|
+
chart_config['filters'].each do |filter|
|
|
40
|
+
field = filter['attr']
|
|
41
|
+
operator = filter['operator']
|
|
42
|
+
value = filter['value']
|
|
43
|
+
|
|
44
|
+
case operator
|
|
45
|
+
when '='
|
|
46
|
+
query = query.where(field => value)
|
|
47
|
+
when '!='
|
|
48
|
+
query = query.where.not(field => value)
|
|
49
|
+
when '>'
|
|
50
|
+
query = query.where("#{field} > ?", value)
|
|
51
|
+
when '>='
|
|
52
|
+
query = query.where("#{field} >= ?", value)
|
|
53
|
+
when '<'
|
|
54
|
+
query = query.where("#{field} < ?", value)
|
|
55
|
+
when '<='
|
|
56
|
+
query = query.where("#{field} <= ?", value)
|
|
57
|
+
when 'contains'
|
|
58
|
+
query = query.where("#{field} LIKE ?", "%#{value}%")
|
|
59
|
+
when 'starts_with'
|
|
60
|
+
query = query.where("#{field} LIKE ?", "#{value}%")
|
|
61
|
+
when 'ends_with'
|
|
62
|
+
query = query.where("#{field} LIKE ?", "%#{value}")
|
|
63
|
+
when 'one_of'
|
|
64
|
+
query = query.where("#{field} IN (?)", value)
|
|
65
|
+
when 'not_one_of'
|
|
66
|
+
query = query.where.not("#{field} IN (?)", value)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
query
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.add_group_clause(query, chart_config)
|
|
74
|
+
group_field = chart_config['x_axis']['attr']
|
|
75
|
+
query.group(group_field)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.parse_series_data(data, chart_config)
|
|
79
|
+
return [] if data.empty?
|
|
80
|
+
|
|
81
|
+
attrs = data[0].attribute_names
|
|
82
|
+
attrs.delete(chart_config['x_axis']['attr'])
|
|
83
|
+
|
|
84
|
+
attrs.map do |attr|
|
|
85
|
+
y_axis = chart_config['y_axes'].find do |col|
|
|
86
|
+
attr == aggregated_attr(col['attr'], col['aggregation'])[1]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
{ name: y_axis['label'], data: data.pluck(attr) }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.parse_category_data(data, chart_config, data_source)
|
|
94
|
+
x_axis_attr = chart_config['x_axis']['attr']
|
|
95
|
+
|
|
96
|
+
translatable = data_source.translated_columns.include?(x_axis_attr.to_sym)
|
|
97
|
+
data.map do |row|
|
|
98
|
+
return row[x_axis_attr] unless translatable
|
|
99
|
+
|
|
100
|
+
I18n.t(row[x_axis_attr], default: row[x_axis_attr])
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Fios
|
|
2
|
+
module Adapters
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class ReportQuery
|
|
5
|
+
def self.add_select_clause(query, data_source, report_config)
|
|
6
|
+
fields = []
|
|
7
|
+
report_config['columns'].each do |column|
|
|
8
|
+
next unless column['selected']
|
|
9
|
+
|
|
10
|
+
if report_config['aggregated']
|
|
11
|
+
fields << column['name'] if column['group_by']
|
|
12
|
+
fields << "COUNT(#{column['name']}) AS 'num_#{column['name']}'" if column['count']
|
|
13
|
+
if column['count_distinct']
|
|
14
|
+
fields << "COUNT(DISTINCT #{column['name']}) AS 'num_uniq_#{column['name']}'"
|
|
15
|
+
end
|
|
16
|
+
if column['average']
|
|
17
|
+
if column['type'].in?(%w[date datetime])
|
|
18
|
+
fields << "FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(#{column['name']}))) AS 'avg_#{column['name']}'"
|
|
19
|
+
else
|
|
20
|
+
fields << "AVG(#{column['name']}) AS 'avg_#{column['name']}'"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
fields << "MIN(#{column['name']}) AS 'min_#{column['name']}'" if column['min']
|
|
24
|
+
fields << "MAX(#{column['name']}) AS 'max_#{column['name']}'" if column['max']
|
|
25
|
+
fields << "SUM(#{column['name']}) AS 'sum_#{column['name']}'" if column['sum']
|
|
26
|
+
else
|
|
27
|
+
fields << column['name']
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
fields = data_source.column_names if fields.empty?
|
|
32
|
+
|
|
33
|
+
query.select(fields)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.add_where_clause(query, report_config)
|
|
37
|
+
return query if report_config['filters'].blank?
|
|
38
|
+
|
|
39
|
+
report_config['filters'].each do |filter|
|
|
40
|
+
field = filter['name']
|
|
41
|
+
operator = filter['operator']
|
|
42
|
+
value = filter['value']
|
|
43
|
+
|
|
44
|
+
case operator
|
|
45
|
+
when '='
|
|
46
|
+
query = query.where(field => value)
|
|
47
|
+
when '!='
|
|
48
|
+
query = query.where.not(field => value)
|
|
49
|
+
when '>'
|
|
50
|
+
query = query.where("#{field} > ?", value)
|
|
51
|
+
when '>='
|
|
52
|
+
query = query.where("#{field} >= ?", value)
|
|
53
|
+
when '<'
|
|
54
|
+
query = query.where("#{field} < ?", value)
|
|
55
|
+
when '<='
|
|
56
|
+
query = query.where("#{field} <= ?", value)
|
|
57
|
+
when 'contains'
|
|
58
|
+
query = query.where("#{field} LIKE ?", "%#{value}%")
|
|
59
|
+
when 'starts_with'
|
|
60
|
+
query = query.where("#{field} LIKE ?", "#{value}%")
|
|
61
|
+
when 'ends_with'
|
|
62
|
+
query = query.where("#{field} LIKE ?", "%#{value}")
|
|
63
|
+
when 'one_of'
|
|
64
|
+
query = query.where("#{field} IN (?)", value)
|
|
65
|
+
when 'not_one_of'
|
|
66
|
+
query = query.where.not("#{field} IN (?)", value)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
query
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.add_group_clause(query, report_config)
|
|
74
|
+
return query unless report_config['aggregated']
|
|
75
|
+
|
|
76
|
+
group_fields = []
|
|
77
|
+
report_config['columns'].each do |column|
|
|
78
|
+
next unless column['selected'] && column['group_by']
|
|
79
|
+
|
|
80
|
+
group_fields << column['name']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
query.group(group_fields)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -7,15 +7,32 @@ module Fios
|
|
|
7
7
|
:active_record
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
def self.fetch_chart_data(data_source, chart)
|
|
11
|
+
chart_config = chart.configuration
|
|
12
|
+
|
|
13
|
+
query = data_source.all
|
|
14
|
+
query = Fios::Adapters::ActiveRecord::ChartQuery.add_select_clause(query, chart_config)
|
|
15
|
+
query = Fios::Adapters::ActiveRecord::ChartQuery.add_where_clause(query, chart_config)
|
|
16
|
+
query = Fios::Adapters::ActiveRecord::ChartQuery.add_group_clause(query, chart_config)
|
|
17
|
+
data = query.to_a
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
series: Fios::Adapters::ActiveRecord::ChartQuery.parse_series_data(data, chart_config),
|
|
21
|
+
categories: Fios::Adapters::ActiveRecord::ChartQuery.parse_category_data(data, chart_config, data_source),
|
|
22
|
+
meta: {
|
|
23
|
+
title: chart.name,
|
|
24
|
+
chart_type: chart_config['chart_type']
|
|
25
|
+
}
|
|
26
|
+
}
|
|
13
27
|
end
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
def self.fetch_report_data(data_source, report)
|
|
30
|
+
report_config = report.configuration
|
|
31
|
+
|
|
32
|
+
query = data_source.all
|
|
33
|
+
query = Fios::Adapters::ActiveRecord::ReportQuery.add_select_clause(query, data_source, report_config)
|
|
34
|
+
query = Fios::Adapters::ActiveRecord::ReportQuery.add_where_clause(query, report_config)
|
|
35
|
+
Fios::Adapters::ActiveRecord::ReportQuery.add_group_clause(query, report_config)
|
|
19
36
|
end
|
|
20
37
|
end
|
|
21
38
|
end
|
data/lib/fios/adapters/base.rb
CHANGED
|
@@ -3,16 +3,18 @@ module Fios
|
|
|
3
3
|
module Base
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
|
-
attr_accessor :dataset, :definition
|
|
7
|
-
|
|
8
6
|
class_methods do
|
|
9
7
|
def adapter_key
|
|
10
8
|
raise NotImplementedError
|
|
11
9
|
end
|
|
12
|
-
end
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
def fetch_chart_data(data_source, chart)
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def fetch_report_data(data_source, report)
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
end
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
|
@@ -2,31 +2,28 @@ module Fios
|
|
|
2
2
|
module Builders
|
|
3
3
|
class ChartBuilder
|
|
4
4
|
def self.build(chart)
|
|
5
|
+
data = fetch_data(chart)
|
|
6
|
+
parse_query_results(data)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.fetch_data(chart)
|
|
5
10
|
chart_config = chart.configuration || {}
|
|
6
11
|
dataset = Dataset.find(chart_config['dataset_id'])
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
query = dataset_class.all
|
|
10
|
-
query = add_select_clause(query, chart_config)
|
|
11
|
-
query = add_where_clause(query, chart_config)
|
|
12
|
-
query = add_group_clause(query, chart_config)
|
|
13
|
-
parse_query_results(query, chart)
|
|
12
|
+
Fios::Services::DatasetFetcher.fetch_chart_data(dataset, chart)
|
|
14
13
|
end
|
|
15
14
|
|
|
16
|
-
def self.parse_query_results(
|
|
17
|
-
data = query.to_a
|
|
18
|
-
|
|
15
|
+
def self.parse_query_results(data)
|
|
19
16
|
{
|
|
20
17
|
'chart': {
|
|
21
|
-
'type':
|
|
18
|
+
'type': data[:meta][:chart_type]
|
|
22
19
|
},
|
|
23
20
|
|
|
24
21
|
'title': {
|
|
25
|
-
'text':
|
|
22
|
+
'text': data[:meta][:title]
|
|
26
23
|
},
|
|
27
24
|
|
|
28
25
|
'xAxis': {
|
|
29
|
-
'categories':
|
|
26
|
+
'categories': data[:categories]
|
|
30
27
|
},
|
|
31
28
|
|
|
32
29
|
'yAxis': {
|
|
@@ -35,116 +32,14 @@ module Fios
|
|
|
35
32
|
}
|
|
36
33
|
},
|
|
37
34
|
|
|
38
|
-
'series':
|
|
35
|
+
'series': data[:series]
|
|
39
36
|
}
|
|
40
37
|
end
|
|
41
38
|
|
|
42
|
-
def self.parse_category_data(data, chart_config)
|
|
43
|
-
x_axis_attr = chart_config['x_axis']['attr']
|
|
44
|
-
|
|
45
|
-
dataset = Dataset.find(chart_config['dataset_id'])
|
|
46
|
-
dataset_class = Fios::Services::DatasetFetcher.fetch(dataset)
|
|
47
|
-
|
|
48
|
-
translatable = dataset_class.translated_columns.include?(x_axis_attr.to_sym)
|
|
49
|
-
data.map do |row|
|
|
50
|
-
return row[x_axis_attr] unless translatable
|
|
51
|
-
|
|
52
|
-
I18n.t(row[x_axis_attr], default: row[x_axis_attr])
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def self.parse_series_data(data, chart_config)
|
|
57
|
-
return [] if data.empty?
|
|
58
|
-
|
|
59
|
-
attrs = data[0].attribute_names
|
|
60
|
-
attrs.delete(chart_config['x_axis']['attr'])
|
|
61
|
-
|
|
62
|
-
attrs.map do |attr|
|
|
63
|
-
y_axis = chart_config['y_axes'].find do |col|
|
|
64
|
-
attr == aggregated_attr(col['attr'], col['aggregation'])[1]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
{ name: y_axis['label'], data: data.pluck(attr) }
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def self.add_select_clause(query, chart_config)
|
|
72
|
-
fields = []
|
|
73
|
-
fields << chart_config['x_axis']['attr']
|
|
74
|
-
|
|
75
|
-
chart_config['y_axes'].each do |column|
|
|
76
|
-
aggregate = aggregated_attr(column['attr'], column['aggregation'])
|
|
77
|
-
fields << "#{aggregate[0]} AS '#{aggregate[1]}'"
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
query.select(fields)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def self.aggregated_attr(attr, aggregation)
|
|
84
|
-
case aggregation
|
|
85
|
-
when 'count'
|
|
86
|
-
["COUNT(#{attr})", "num_#{attr}"]
|
|
87
|
-
when 'count_distinct'
|
|
88
|
-
["COUNT(DISTINCT #{attr})", "num_uniq_#{attr}"]
|
|
89
|
-
when 'average'
|
|
90
|
-
["AVG(#{attr})", "avg_#{attr}"]
|
|
91
|
-
when 'min'
|
|
92
|
-
["MIN(#{attr})", "min_#{attr}"]
|
|
93
|
-
when 'max'
|
|
94
|
-
["MAX(#{attr})", "max_#{attr}"]
|
|
95
|
-
when 'sum'
|
|
96
|
-
["SUM(#{attr})", "sum_#{attr}"]
|
|
97
|
-
else
|
|
98
|
-
attr
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def self.add_where_clause(query, chart_config)
|
|
103
|
-
return query if chart_config['filters'].blank?
|
|
104
|
-
|
|
105
|
-
chart_config['filters'].each do |filter|
|
|
106
|
-
field = filter['attr']
|
|
107
|
-
operator = filter['operator']
|
|
108
|
-
value = filter['value']
|
|
109
|
-
|
|
110
|
-
case operator
|
|
111
|
-
when '='
|
|
112
|
-
query = query.where(field => value)
|
|
113
|
-
when '!='
|
|
114
|
-
query = query.where.not(field => value)
|
|
115
|
-
when '>'
|
|
116
|
-
query = query.where("#{field} > ?", value)
|
|
117
|
-
when '>='
|
|
118
|
-
query = query.where("#{field} >= ?", value)
|
|
119
|
-
when '<'
|
|
120
|
-
query = query.where("#{field} < ?", value)
|
|
121
|
-
when '<='
|
|
122
|
-
query = query.where("#{field} <= ?", value)
|
|
123
|
-
when 'contains'
|
|
124
|
-
query = query.where("#{field} LIKE ?", "%#{value}%")
|
|
125
|
-
when 'starts_with'
|
|
126
|
-
query = query.where("#{field} LIKE ?", "#{value}%")
|
|
127
|
-
when 'ends_with'
|
|
128
|
-
query = query.where("#{field} LIKE ?", "%#{value}")
|
|
129
|
-
when 'one_of'
|
|
130
|
-
query = query.where("#{field} IN (?)", value)
|
|
131
|
-
when 'not_one_of'
|
|
132
|
-
query = query.where.not("#{field} IN (?)", value)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
query
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def self.add_group_clause(query, chart_config)
|
|
140
|
-
group_field = chart_config['x_axis']['attr']
|
|
141
|
-
query.group(group_field)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
39
|
def self.build_csv(chart)
|
|
145
40
|
{
|
|
146
41
|
headers: csv_headers(chart),
|
|
147
|
-
rows:
|
|
42
|
+
rows: fetch_data(chart)
|
|
148
43
|
}
|
|
149
44
|
end
|
|
150
45
|
|
|
@@ -1,111 +1,28 @@
|
|
|
1
1
|
module Fios
|
|
2
2
|
module Builders
|
|
3
3
|
class ReportBuilder
|
|
4
|
-
def self.build(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
dataset = Dataset.find(report_config['dataset_id'])
|
|
8
|
-
dataset_class = Fios::Services::DatasetFetcher.fetch(dataset)
|
|
9
|
-
|
|
10
|
-
query = dataset_class.all
|
|
11
|
-
query = add_select_clause(query, dataset_class, report_config)
|
|
12
|
-
query = add_where_clause(query, report_config)
|
|
13
|
-
add_group_clause(query, report_config)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.add_select_clause(query, dataset_class, report_config)
|
|
17
|
-
fields = []
|
|
18
|
-
report_config['columns'].each do |column|
|
|
19
|
-
next unless column['selected']
|
|
20
|
-
|
|
21
|
-
if report_config['aggregated']
|
|
22
|
-
fields << column['name'] if column['group_by']
|
|
23
|
-
fields << "COUNT(#{column['name']}) AS 'num_#{column['name']}'" if column['count']
|
|
24
|
-
if column['count_distinct']
|
|
25
|
-
fields << "COUNT(DISTINCT #{column['name']}) AS 'num_uniq_#{column['name']}'"
|
|
26
|
-
end
|
|
27
|
-
if column['average']
|
|
28
|
-
if column['type'].in?(%w[date datetime])
|
|
29
|
-
fields << "FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(#{column['name']}))) AS 'avg_#{column['name']}'"
|
|
30
|
-
else
|
|
31
|
-
fields << "AVG(#{column['name']}) AS 'avg_#{column['name']}'"
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
fields << "MIN(#{column['name']}) AS 'min_#{column['name']}'" if column['min']
|
|
35
|
-
fields << "MAX(#{column['name']}) AS 'max_#{column['name']}'" if column['max']
|
|
36
|
-
fields << "SUM(#{column['name']}) AS 'sum_#{column['name']}'" if column['sum']
|
|
37
|
-
else
|
|
38
|
-
fields << column['name']
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
fields = dataset_class.column_names if fields.empty?
|
|
43
|
-
|
|
44
|
-
query.select(fields)
|
|
4
|
+
def self.build(report)
|
|
5
|
+
fetch_data(report)
|
|
45
6
|
end
|
|
46
7
|
|
|
47
|
-
def self.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
field = filter['name']
|
|
52
|
-
operator = filter['operator']
|
|
53
|
-
value = filter['value']
|
|
54
|
-
|
|
55
|
-
case operator
|
|
56
|
-
when '='
|
|
57
|
-
query = query.where(field => value)
|
|
58
|
-
when '!='
|
|
59
|
-
query = query.where.not(field => value)
|
|
60
|
-
when '>'
|
|
61
|
-
query = query.where("#{field} > ?", value)
|
|
62
|
-
when '>='
|
|
63
|
-
query = query.where("#{field} >= ?", value)
|
|
64
|
-
when '<'
|
|
65
|
-
query = query.where("#{field} < ?", value)
|
|
66
|
-
when '<='
|
|
67
|
-
query = query.where("#{field} <= ?", value)
|
|
68
|
-
when 'contains'
|
|
69
|
-
query = query.where("#{field} LIKE ?", "%#{value}%")
|
|
70
|
-
when 'starts_with'
|
|
71
|
-
query = query.where("#{field} LIKE ?", "#{value}%")
|
|
72
|
-
when 'ends_with'
|
|
73
|
-
query = query.where("#{field} LIKE ?", "%#{value}")
|
|
74
|
-
when 'one_of'
|
|
75
|
-
query = query.where("#{field} IN (?)", value)
|
|
76
|
-
when 'not_one_of'
|
|
77
|
-
query = query.where.not("#{field} IN (?)", value)
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
query
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def self.add_group_clause(query, report_config)
|
|
85
|
-
return query unless report_config['aggregated']
|
|
86
|
-
|
|
87
|
-
group_fields = []
|
|
88
|
-
report_config['columns'].each do |column|
|
|
89
|
-
next unless column['selected'] && column['group_by']
|
|
90
|
-
|
|
91
|
-
group_fields << column['name']
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
query.group(group_fields)
|
|
8
|
+
def self.fetch_data(report)
|
|
9
|
+
report_config = report.configuration || {}
|
|
10
|
+
dataset = Dataset.find(report_config['dataset_id'])
|
|
11
|
+
Fios::Services::DatasetFetcher.fetch_report_data(dataset, report)
|
|
95
12
|
end
|
|
96
13
|
|
|
97
|
-
def self.build_csv(
|
|
14
|
+
def self.build_csv(report)
|
|
98
15
|
{
|
|
99
|
-
headers: csv_headers(
|
|
100
|
-
rows:
|
|
16
|
+
headers: csv_headers(report),
|
|
17
|
+
rows: fetch_data(report)
|
|
101
18
|
}
|
|
102
19
|
end
|
|
103
20
|
|
|
104
|
-
def self.csv_headers(
|
|
105
|
-
report_config =
|
|
21
|
+
def self.csv_headers(report)
|
|
22
|
+
report_config = report.configuration || {}
|
|
106
23
|
|
|
107
24
|
dataset = Dataset.find(report_config['dataset_id'])
|
|
108
|
-
dataset_class = Fios::Services::DatasetFetcher.
|
|
25
|
+
dataset_class = Fios::Services::DatasetFetcher.fetch_report_data(dataset, report)
|
|
109
26
|
|
|
110
27
|
return dataset_class.column_names if report_config['columns'].blank?
|
|
111
28
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
module Fios
|
|
2
2
|
module Services
|
|
3
3
|
class DatasetFetcher
|
|
4
|
-
def self.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
adapter.
|
|
8
|
-
|
|
4
|
+
def self.fetch_chart_data(dataset, chart)
|
|
5
|
+
adapter = Fios::Adapters::Registry.fetch(dataset.adapter)
|
|
6
|
+
data_source = Fios::Definitions::Registry.fetch(dataset.slug)
|
|
7
|
+
data = adapter.fetch_chart_data(data_source, chart)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.fetch_report_data(dataset, report)
|
|
11
|
+
adapter = Fios::Adapters::Registry.fetch(dataset.adapter)
|
|
12
|
+
data_source = Fios::Definitions::Registry.fetch(dataset.slug)
|
|
13
|
+
data = adapter.fetch_report_data(data_source, report)
|
|
9
14
|
end
|
|
10
15
|
end
|
|
11
16
|
end
|
data/lib/fios/version.rb
CHANGED
data/lib/fios.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "active_support/concern"
|
|
|
5
5
|
require "rails"
|
|
6
6
|
|
|
7
7
|
require_relative "fios/adapters/base"
|
|
8
|
+
require_relative "fios/adapters/active_record/chart_query"
|
|
9
|
+
require_relative "fios/adapters/active_record/report_query"
|
|
8
10
|
require_relative "fios/adapters/active_record_adapter"
|
|
9
11
|
require_relative "fios/adapters/registry"
|
|
10
12
|
require_relative "fios/builders/chart_builder"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fios
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark Harbison
|
|
@@ -24,7 +24,8 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '6.1'
|
|
27
|
-
description:
|
|
27
|
+
description: A data analytics framework for building charts, dashboards, and reports
|
|
28
|
+
in Ruby.
|
|
28
29
|
email:
|
|
29
30
|
- mark@tyne-solutions.com
|
|
30
31
|
executables: []
|
|
@@ -45,6 +46,8 @@ files:
|
|
|
45
46
|
- bin/setup
|
|
46
47
|
- fios.gemspec
|
|
47
48
|
- lib/fios.rb
|
|
49
|
+
- lib/fios/adapters/active_record/chart_query.rb
|
|
50
|
+
- lib/fios/adapters/active_record/report_query.rb
|
|
48
51
|
- lib/fios/adapters/active_record_adapter.rb
|
|
49
52
|
- lib/fios/adapters/base.rb
|
|
50
53
|
- lib/fios/adapters/registry.rb
|
|
@@ -84,7 +87,8 @@ requirements: []
|
|
|
84
87
|
rubygems_version: 3.4.19
|
|
85
88
|
signing_key:
|
|
86
89
|
specification_version: 4
|
|
87
|
-
summary:
|
|
90
|
+
summary: A data analytics framework for building charts, dashboards, and reports in
|
|
91
|
+
Ruby.
|
|
88
92
|
test_files:
|
|
89
93
|
- spec/fios_spec.rb
|
|
90
94
|
- spec/spec_helper.rb
|