forest_admin_datasource_toolkit 1.0.0.pre.beta.21 → 1.0.0.pre.beta.23

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ed1e878cea7868c7a6443190c5dd53b880e154dcc5b4088532a156151464263
4
- data.tar.gz: 41f2bf182d1851db0afb7a53a943d023b4698fa33fbfd584bee4ca7e40b702ac
3
+ metadata.gz: 786b58c3bc21c7187d7a95025aae6229de6c04bac42549cede55f060be797d86
4
+ data.tar.gz: 477ffdc3ed1eba5e5240e641741f9f7cbd311373e35594438ccc452584a75830
5
5
  SHA512:
6
- metadata.gz: a8376956ef1d90ae0f54d40aeb5ef81eef793d3aa16e7cd1da293cf927c1155ff7e7f8f848843175f21de6eabce34b11f934335665d29f99003e1d9b002835b6
7
- data.tar.gz: 3b89ff9d179863b54a8000b3089acf05dbf6faff27d89fb6692b32107db752ad6ae9778ea34caebcdc133d6cba90e305cb295950666d6fc01f1609c6817658fc
6
+ metadata.gz: 46282da2e6d7221c853d234d382a3712a4fcb0e0340c06f93ba97f2a9ca26967a0adc0e153e6b904dfe39c4aaadcb31ffe0a3731ae6a097e3520120dbdaa0e16
7
+ data.tar.gz: 52c33562da9679ea958fec285158dc6c12f5fb7ff9a3076f2724951bae500e99debb10925a562add7daeab4d749102494b46c6a33eaaaee22e54c5fb4c861984
@@ -46,5 +46,11 @@ module ForestAdminDatasourceToolkit
46
46
  add_field(name, field)
47
47
  end
48
48
  end
49
+
50
+ def add_action(name, action)
51
+ raise Exceptions::ForestException, "Action #{name} already defined in collection" if @actions[key]
52
+
53
+ @actions[name] = action
54
+ end
49
55
  end
50
56
  end
@@ -0,0 +1,11 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class Chart
5
+ def serialize
6
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class LeaderboardChart
5
+ include ForestAdminDatasourceToolkit::Validations
6
+
7
+ attr_reader :data
8
+
9
+ def initialize(data)
10
+ super()
11
+ @data = data
12
+ end
13
+
14
+ def serialize
15
+ data.each do |item|
16
+ ChartValidator.validate?(!item.key?(:key) || !item.key?(:value), item, "'key', 'value'")
17
+ end
18
+
19
+ data
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class LineChart
5
+ include ForestAdminDatasourceToolkit::Validations
6
+
7
+ attr_reader :data
8
+
9
+ def initialize(data)
10
+ super()
11
+ @data = data
12
+ end
13
+
14
+ def serialize
15
+ data.each do |item|
16
+ ChartValidator.validate?(!item.key?(:label) || !item.key?(:values), item, "'label', 'values'")
17
+ end
18
+
19
+ data
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class ObjectiveChart
5
+ attr_reader :value, :objective
6
+
7
+ def initialize(value, objective = nil)
8
+ super()
9
+ @value = value
10
+ @objective = objective
11
+ end
12
+
13
+ def serialize
14
+ result = { value: value }
15
+ result[:objective] = objective if objective
16
+
17
+ result
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class PercentageChart
5
+ attr_reader :value
6
+
7
+ def initialize(value)
8
+ super()
9
+ @value = value
10
+ end
11
+
12
+ def serialize
13
+ value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class PieChart
5
+ include ForestAdminDatasourceToolkit::Validations
6
+
7
+ attr_reader :data
8
+
9
+ def initialize(data)
10
+ super()
11
+ @data = data
12
+ end
13
+
14
+ def serialize
15
+ data.each do |item|
16
+ ChartValidator.validate?(!item.key?(:key) || !item.key?(:value), item, "'key', 'value'")
17
+ end
18
+
19
+ data
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class SmartChart
5
+ attr_reader :data
6
+
7
+ def initialize(data)
8
+ super()
9
+ @data = data
10
+ end
11
+
12
+ def serialize
13
+ data
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Components
3
+ module Charts
4
+ class ValueChart
5
+ attr_reader :value, :previous_value
6
+
7
+ def initialize(value, previous_value = nil)
8
+ super()
9
+ @value = value
10
+ @previous_value = previous_value
11
+ end
12
+
13
+ def serialize
14
+ { countCurrent: value, countPrevious: previous_value }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,6 @@
1
+ require 'active_support/all'
2
+ require 'active_support/core_ext/numeric/time'
3
+
1
4
  module ForestAdminDatasourceToolkit
2
5
  module Components
3
6
  module Query
@@ -17,6 +20,171 @@ module ForestAdminDatasourceToolkit
17
20
 
18
21
  raise ForestException, "Aggregate operation #{operation} not allowed"
19
22
  end
23
+
24
+ def projection
25
+ aggregate_fields = []
26
+ aggregate_fields << field if field
27
+
28
+ groups.each do |group|
29
+ aggregate_fields << group[:field]
30
+ end
31
+
32
+ Projection.new(aggregate_fields)
33
+ end
34
+
35
+ def replace_fields(handler)
36
+ result = clone
37
+ result.field = handler.call(result.field) if result.field
38
+ result.groups = result.groups.map do |group|
39
+ {
40
+ field: handler.call(group[:field]),
41
+ operation: group[:operation] || nil
42
+ }
43
+ end
44
+ result
45
+ end
46
+
47
+ def override(**args)
48
+ Aggregation.new(**to_h.merge(args))
49
+ end
50
+
51
+ def apply(records, timezone, limit = nil)
52
+ rows = format_summaries(create_summaries(records, timezone))
53
+ rows.sort do |r1, r2|
54
+ if r1[:value] == r2[:value]
55
+ 0
56
+ else
57
+ r1[:value] < r2[:value] ? 1 : -1
58
+ end
59
+ end
60
+
61
+ rows = rows[0..limit - 1] if limit && rows.size > limit
62
+
63
+ rows
64
+ end
65
+
66
+ def nest(prefix = nil)
67
+ return self unless prefix
68
+
69
+ nested_field = nil
70
+ nested_groups = []
71
+ nested_field = "#{prefix}:#{field}" if field
72
+
73
+ if groups.size.positive?
74
+ nested_groups = groups.map do |item|
75
+ {
76
+ field: "#{prefix}:#{item[:field]}",
77
+ operation: item[:operation]
78
+ }
79
+ end
80
+ end
81
+
82
+ self.class.new(operation: operation, field: nested_field, groups: nested_groups)
83
+ end
84
+
85
+ def to_h
86
+ {
87
+ operation: operation,
88
+ field: field,
89
+ groups: groups
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ def create_summaries(records, timezone)
96
+ grouping_map = {}
97
+
98
+ records.each do |record|
99
+ group = create_group(record, timezone)
100
+ unique_key = Digest::SHA1.hexdigest(group.to_json)
101
+ summary = grouping_map[unique_key] || create_summary(group)
102
+
103
+ update_summary_in_place(summary, record)
104
+
105
+ grouping_map[unique_key] = summary
106
+ end
107
+
108
+ grouping_map.values
109
+ end
110
+
111
+ def format_summaries(summaries)
112
+ if operation == 'Avg'
113
+ summaries
114
+ .select { |summary| (summary['Count']).positive? }
115
+ .map do |summary|
116
+ {
117
+ group: summary['group'],
118
+ value: summary['Sum'] / summary['Count']
119
+ }
120
+ end
121
+ else
122
+ summaries.map do |summary|
123
+ {
124
+ group: summary['group'],
125
+ value: operation == 'Count' && !field ? summary['starCount'] : summary[operation]
126
+ }
127
+ end
128
+ end
129
+ end
130
+
131
+ def create_group(record, timezone)
132
+ group = {}
133
+
134
+ groups.each do |value|
135
+ group_value = record[value[:field]]
136
+ group[value[:field]] = apply_date_operation(group_value, value[:operation], timezone)
137
+ end
138
+
139
+ group
140
+ end
141
+
142
+ def apply_date_operation(value, operation, timezone)
143
+ return value unless operation
144
+
145
+ case operation
146
+ when 'Year'
147
+ DateTime.parse(value).in_time_zone(timezone).strftime('%Y-01-01')
148
+ when 'Month'
149
+ DateTime.parse(value).in_time_zone(timezone).strftime('%Y-%m-01')
150
+ when 'Day'
151
+ DateTime.parse(value).in_time_zone(timezone).strftime('%Y-%m-%d')
152
+ when 'Week'
153
+ DateTime.parse(value).in_time_zone(timezone).beginning_of_month.strftime('%Y-%m-%d')
154
+ else
155
+ value
156
+ end
157
+ end
158
+
159
+ def create_summary(group)
160
+ {
161
+ 'group' => group,
162
+ 'starCount' => 0,
163
+ 'Count' => 0,
164
+ 'Sum' => 0,
165
+ 'Min' => nil,
166
+ 'Max' => nil
167
+ }
168
+ end
169
+
170
+ def update_summary_in_place(summary, record)
171
+ summary['starCount'] += 1
172
+
173
+ return unless field
174
+
175
+ value = ForestAdminDatasourceToolkit::Utils::Record.field_value(record, field)
176
+
177
+ if value
178
+ min = summary['Min']
179
+ max = summary['Max']
180
+
181
+ summary['Count'] += 1
182
+ summary['Min'] = value if min.nil? || value < min
183
+ summary['Max'] = value if max.nil? || value < max
184
+ end
185
+
186
+ summary['Sum'] += value if value.is_a?(Numeric)
187
+ end
20
188
  end
21
189
  end
22
190
  end
@@ -41,18 +41,18 @@ module ForestAdminDatasourceToolkit
41
41
  depends_on = alt[:depends_on]
42
42
  valid = alt[:for_types].nil? || alt[:for_types].include?(column_type)
43
43
 
44
- if valid && !visited.include?(alt)
45
- depends_replacers = depends_on.map do |replacement|
46
- get_replacer(replacement, filter_operators, column_type, visited + [alt])
47
- end
44
+ next unless valid && !visited.include?(alt)
48
45
 
49
- if depends_replacers.all? { |r| !r.nil? }
50
- return lambda { |leaf, timezone|
51
- replacer.call(leaf).replace_leafs do |sub_leaf|
52
- depends_replacers[depends_on.index(sub_leaf.operator)].call(sub_leaf, timezone)
53
- end
54
- }
55
- end
46
+ depends_replacers = depends_on.map do |replacement|
47
+ get_replacer(replacement, filter_operators, column_type, visited + [alt])
48
+ end
49
+
50
+ if depends_replacers.all? { |r| !r.nil? }
51
+ return lambda { |leaf, timezone|
52
+ replacer.call(leaf).replace_leafs do |sub_leaf|
53
+ depends_replacers[depends_on.index(sub_leaf.operator)].call(sub_leaf, timezone)
54
+ end
55
+ }
56
56
  end
57
57
  end
58
58
 
@@ -3,7 +3,7 @@ module ForestAdminDatasourceToolkit
3
3
  class ForestException < RuntimeError
4
4
  def initialize(msg = '')
5
5
  msg = "🌳🌳🌳 #{msg}"
6
- super msg
6
+ super(msg)
7
7
  end
8
8
  end
9
9
  end
@@ -106,6 +106,23 @@ module ForestAdminDatasourceToolkit
106
106
  nil
107
107
  end
108
108
 
109
+ def self.get_through_origin(collection, relation_name)
110
+ relation = collection.fields[relation_name]
111
+ raise ForestException, 'Relation must be many to many' unless relation.is_a?(ManyToManySchema)
112
+
113
+ through_collection = collection.datasource.collection(relation.through_collection)
114
+ through_collection.fields.select do |field_name, field|
115
+ if field.is_a?(ManyToOneSchema) &&
116
+ field.foreign_collection == collection.name &&
117
+ field.foreign_key == relation.origin_key &&
118
+ field.foreign_key_target == relation.origin_key_target
119
+ return field_name
120
+ end
121
+ end
122
+
123
+ nil
124
+ end
125
+
109
126
  def self.list_relation(collection, id, relation_name, caller, foreign_filter, projection)
110
127
  relation = collection.fields[relation_name]
111
128
  foreign_collection = collection.datasource.collection(relation.foreign_collection)
@@ -0,0 +1,15 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Validations
3
+ class ChartValidator
4
+ def self.validate?(condition, result, key_names)
5
+ if condition
6
+ result_keys = result.keys.join(',')
7
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
8
+ "The result columns must be named '#{key_names}' instead of '#{result_keys}'"
9
+ end
10
+
11
+ true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module ForestAdminDatasourceToolkit
2
- VERSION = "1.0.0-beta.21"
2
+ VERSION = "1.0.0-beta.23"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_datasource_toolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.beta.21
4
+ version: 1.0.0.pre.beta.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-11-20 00:00:00.000000000 Z
12
+ date: 2023-12-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -56,6 +56,14 @@ files:
56
56
  - lib/forest_admin_datasource_toolkit.rb
57
57
  - lib/forest_admin_datasource_toolkit/collection.rb
58
58
  - lib/forest_admin_datasource_toolkit/components/caller.rb
59
+ - lib/forest_admin_datasource_toolkit/components/charts/chart.rb
60
+ - lib/forest_admin_datasource_toolkit/components/charts/leaderboard_chart.rb
61
+ - lib/forest_admin_datasource_toolkit/components/charts/line_chart.rb
62
+ - lib/forest_admin_datasource_toolkit/components/charts/objective_chart.rb
63
+ - lib/forest_admin_datasource_toolkit/components/charts/percentage_chart.rb
64
+ - lib/forest_admin_datasource_toolkit/components/charts/pie_chart.rb
65
+ - lib/forest_admin_datasource_toolkit/components/charts/smart_chart.rb
66
+ - lib/forest_admin_datasource_toolkit/components/charts/value_chart.rb
59
67
  - lib/forest_admin_datasource_toolkit/components/contracts/collection_contract.rb
60
68
  - lib/forest_admin_datasource_toolkit/components/contracts/datasource_contract.rb
61
69
  - lib/forest_admin_datasource_toolkit/components/query/aggregation.rb
@@ -85,6 +93,7 @@ files:
85
93
  - lib/forest_admin_datasource_toolkit/utils/collection.rb
86
94
  - lib/forest_admin_datasource_toolkit/utils/record.rb
87
95
  - lib/forest_admin_datasource_toolkit/utils/schema.rb
96
+ - lib/forest_admin_datasource_toolkit/validations/chart_validator.rb
88
97
  - lib/forest_admin_datasource_toolkit/version.rb
89
98
  - sig/forest_admin_datasource_toolkit.rbs
90
99
  - sig/forest_admin_datasource_toolkit/collection.rbs