solidus_admin_insights 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +54 -0
- data/Gemfile +2 -0
- data/LICENSE +26 -0
- data/README.md +71 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/spree/backend/jquery.tablesorter.min.js +4 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights.js +2 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/paginator.js +128 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/report_loader.js +265 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/searcher.js +72 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/table_sorter.js +47 -0
- data/app/assets/javascripts/spree/backend/tmpl.js +87 -0
- data/app/assets/javascripts/spree/frontend/solidus_admin_insights.js +2 -0
- data/app/assets/stylesheets/spree/backend/override_pdf.css +71 -0
- data/app/assets/stylesheets/spree/backend/solidus_admin_insights.css +132 -0
- data/app/assets/stylesheets/spree/frontend/solidus_admin_insights.css +4 -0
- data/app/controllers/spree/admin/insights_controller.rb +103 -0
- data/app/helpers/spree/admin/base_helper_decorator.rb +17 -0
- data/app/models/spree/app_configuration_decorator.rb +3 -0
- data/app/models/spree/product_decorator.rb +3 -0
- data/app/models/spree/promotion_action_decorator.rb +3 -0
- data/app/models/spree/return_authorization_decorator.rb +4 -0
- data/app/permissions/spree/permission_sets/insight_display.rb +9 -0
- data/app/reports/spree/best_selling_products_report.rb +42 -0
- data/app/reports/spree/cart_additions_report.rb +36 -0
- data/app/reports/spree/cart_removals_report.rb +36 -0
- data/app/reports/spree/cart_updations_report.rb +40 -0
- data/app/reports/spree/payment_method_transactions_conversion_rate_report.rb +74 -0
- data/app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb +39 -0
- data/app/reports/spree/payment_method_transactions_report.rb +60 -0
- data/app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb +36 -0
- data/app/reports/spree/product_views_report.rb +46 -0
- data/app/reports/spree/product_views_to_cart_additions_report.rb +53 -0
- data/app/reports/spree/product_views_to_purchases_report.rb +54 -0
- data/app/reports/spree/promotional_cost_report.rb +84 -0
- data/app/reports/spree/promotional_cost_report/promotional_cost_chart.rb +37 -0
- data/app/reports/spree/promotional_cost_report/usage_count_chart.rb +41 -0
- data/app/reports/spree/report.rb +131 -0
- data/app/reports/spree/report/chart.rb +11 -0
- data/app/reports/spree/report/configuration.rb +40 -0
- data/app/reports/spree/report/date_slicer.rb +61 -0
- data/app/reports/spree/report/observation.rb +49 -0
- data/app/reports/spree/report/query_fragments.rb +45 -0
- data/app/reports/spree/report/query_time_scale.rb +19 -0
- data/app/reports/spree/report/result.rb +100 -0
- data/app/reports/spree/report/timed_observation.rb +47 -0
- data/app/reports/spree/report/timed_result.rb +48 -0
- data/app/reports/spree/returned_products_report.rb +37 -0
- data/app/reports/spree/sales_performance_report.rb +107 -0
- data/app/reports/spree/sales_performance_report/profit_loss_chart.rb +37 -0
- data/app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb +36 -0
- data/app/reports/spree/sales_performance_report/sale_cost_price_chart.rb +48 -0
- data/app/reports/spree/sales_tax_report.rb +64 -0
- data/app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb +39 -0
- data/app/reports/spree/shipping_cost_report.rb +89 -0
- data/app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb +38 -0
- data/app/reports/spree/trending_search_report.rb +50 -0
- data/app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb +41 -0
- data/app/reports/spree/unique_purchases_report.rb +39 -0
- data/app/reports/spree/user_pool_report.rb +66 -0
- data/app/reports/spree/user_pool_report/distribution_column_chart.rb +65 -0
- data/app/reports/spree/users_not_converted_report.rb +48 -0
- data/app/reports/spree/users_who_recently_purchased_report.rb +69 -0
- data/app/services/spree/report_generation_service.rb +27 -0
- data/app/views/spree/admin/insights/_chart.html.erb +4 -0
- data/app/views/spree/admin/insights/download.pdf.erb +27 -0
- data/app/views/spree/admin/insights/index.html.erb +82 -0
- data/app/views/spree/admin/insights/search/_product_views_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_search_form.html.erb +39 -0
- data/app/views/spree/admin/insights/search/_trending_searches_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_not_converted_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_who_have_not_purchased_recently_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_who_recently_purchased_search.html.erb +13 -0
- data/app/views/spree/admin/shared/_insights_side_menu.html.erb +5 -0
- data/app/views/spree/admin/shared/sub_menu/_insight.html.erb +7 -0
- data/app/views/spree/admin/templates/insights/_paginator.template +11 -0
- data/app/views/spree/admin/templates/insights/_search.template +76 -0
- data/app/views/spree/admin/templates/insights/_show.template +49 -0
- data/app/views/spree/layouts/pdf.html.erb +9 -0
- data/bin/rails +7 -0
- data/config/initializers/add_to_sidebar.rb +14 -0
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/mime_types.rb +2 -0
- data/config/initializers/wicked_pdf.rb +21 -0
- data/config/locales/en.yml +167 -0
- data/config/routes.rb +6 -0
- data/lib/generators/solidus_admin_insights/install/install_generator.rb +36 -0
- data/lib/generators/solidus_admin_insights/install/solidus_admin_insights.rb +22 -0
- data/lib/solidus_admin_insights.rb +14 -0
- data/lib/solidus_admin_insights/engine.rb +27 -0
- data/lib/solidus_admin_insights/factories.rb +6 -0
- data/solidus_admin_insights.gemspec +42 -0
- data/spec/spec_helper.rb +93 -0
- metadata +419 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
class Spree::SalesPerformanceReport::ProfitLossChart
|
2
|
+
def initialize(result)
|
3
|
+
time_dim = result.time_dimension
|
4
|
+
@time_series = result.observations.collect(&time_dim)
|
5
|
+
@data = result.observations.collect(&:profit_loss)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_h
|
9
|
+
{
|
10
|
+
id: 'profit-loss',
|
11
|
+
json: {
|
12
|
+
title: {
|
13
|
+
useHTML: true,
|
14
|
+
text: "<span class='chart-title'>Profit/Loss</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Track the profit or loss value'></span>"
|
15
|
+
},
|
16
|
+
xAxis: { categories: @time_series },
|
17
|
+
yAxis: {
|
18
|
+
title: { text: 'Value($)' }
|
19
|
+
},
|
20
|
+
legend: {
|
21
|
+
layout: 'vertical',
|
22
|
+
align: 'right',
|
23
|
+
verticalAlign: 'middle',
|
24
|
+
borderWidth: 0
|
25
|
+
},
|
26
|
+
series: [
|
27
|
+
{
|
28
|
+
name: 'Profit Loss',
|
29
|
+
tooltip: { valuePrefix: '$' },
|
30
|
+
data: @data
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Spree::SalesPerformanceReport::ProfitLossPercentChart
|
2
|
+
def initialize(result)
|
3
|
+
time_dim = result.time_dimension
|
4
|
+
@time_series = result.observations.collect(&time_dim)
|
5
|
+
@data = result.observations.collect(&:profit_loss_percent)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_h
|
9
|
+
{
|
10
|
+
id: 'profit-loss-percent',
|
11
|
+
json: {
|
12
|
+
title: {
|
13
|
+
useHTML: true,
|
14
|
+
text: "<span class='chart-title'>Profit/Loss %</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Track the profit or loss %age to create a projection'></span>"
|
15
|
+
},
|
16
|
+
xAxis: { categories: @time_series },
|
17
|
+
yAxis: {
|
18
|
+
title: { text: 'Percentage(%)' }
|
19
|
+
},
|
20
|
+
legend: {
|
21
|
+
layout: 'vertical',
|
22
|
+
align: 'right',
|
23
|
+
verticalAlign: 'middle',
|
24
|
+
borderWidth: 0
|
25
|
+
},
|
26
|
+
series: [
|
27
|
+
{
|
28
|
+
name: 'Profit Loss Percent(%)',
|
29
|
+
tooltip: { valueSuffix: '%' },
|
30
|
+
data: @data
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Spree::SalesPerformanceReport::SaleCostPriceChart
|
2
|
+
def initialize(result)
|
3
|
+
time_dim = result.time_dimension
|
4
|
+
@time_series = result.observations.collect(&time_dim)
|
5
|
+
@sale_price = result.observations.collect(&:sale_price)
|
6
|
+
@cost_price = result.observations.collect(&:cost_price)
|
7
|
+
@promotion_discount = result.observations.collect(&:promotion_discount)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
{
|
12
|
+
id: 'sale-price',
|
13
|
+
json: {
|
14
|
+
chart: { type: 'column' },
|
15
|
+
title: {
|
16
|
+
useHTML: true,
|
17
|
+
text: "<span class='chart-title'>Sales Performance %</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Compare the Selling price, cost price and promotional cost over a period of time'></span>"
|
18
|
+
},
|
19
|
+
xAxis: { categories: @time_series },
|
20
|
+
yAxis: {
|
21
|
+
title: { text: 'Value($)' }
|
22
|
+
},
|
23
|
+
tooltip: { valuePrefix: '$' },
|
24
|
+
legend: {
|
25
|
+
layout: 'vertical',
|
26
|
+
align: 'right',
|
27
|
+
verticalAlign: 'middle',
|
28
|
+
borderWidth: 0
|
29
|
+
},
|
30
|
+
series: [
|
31
|
+
{
|
32
|
+
name: 'Sale Price',
|
33
|
+
data: @sale_price
|
34
|
+
},
|
35
|
+
{
|
36
|
+
name: 'Cost Price',
|
37
|
+
data: @cost_price
|
38
|
+
},
|
39
|
+
{
|
40
|
+
name: 'Promotional Cost',
|
41
|
+
data: @promotion_discount
|
42
|
+
}
|
43
|
+
]
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Spree
|
2
|
+
class SalesTaxReport < Spree::Report
|
3
|
+
HEADERS = { zone_name: :string, sales_tax: :integer }
|
4
|
+
SEARCH_ATTRIBUTES = { start_date: :taxation_from, end_date: :taxation_till }
|
5
|
+
SORTABLE_ATTRIBUTES = []
|
6
|
+
|
7
|
+
class Result < Spree::Report::TimedResult
|
8
|
+
charts MonthlySalesTaxComparisonChart
|
9
|
+
|
10
|
+
def build_empty_observations
|
11
|
+
super
|
12
|
+
@_zones = @results.collect { |r| r['zone_name'] }.uniq
|
13
|
+
@observations = @_zones.collect do |zone|
|
14
|
+
@observations.collect do |observation|
|
15
|
+
_d_observation = observation.dup
|
16
|
+
_d_observation.zone_name = zone
|
17
|
+
_d_observation.sales_tax = 0
|
18
|
+
_d_observation
|
19
|
+
end
|
20
|
+
end.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
class Observation < Spree::Report::TimedObservation
|
24
|
+
observation_fields [:zone_name, :sales_tax]
|
25
|
+
|
26
|
+
def describes?(result, time_scale)
|
27
|
+
(zone_name == result['zone_name']) && super
|
28
|
+
end
|
29
|
+
|
30
|
+
def sales_tax
|
31
|
+
@sales_tax.to_f
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def report_query
|
38
|
+
Spree::Report::QueryFragments
|
39
|
+
.from_subquery(tax_adjustments)
|
40
|
+
.group(*time_scale_columns_to_s, 'zone_name')
|
41
|
+
.order(*time_scale_columns)
|
42
|
+
.project(
|
43
|
+
'zone_name',
|
44
|
+
*time_scale_columns,
|
45
|
+
'SUM(sales_tax) as sales_tax'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
private def tax_adjustments
|
50
|
+
Spree::TaxRate
|
51
|
+
.joins(:adjustments)
|
52
|
+
.joins(:zone)
|
53
|
+
.where(spree_adjustments: { adjustable_type: 'Spree::LineItem' } )
|
54
|
+
.where(spree_adjustments: { created_at: reporting_period })
|
55
|
+
.select(
|
56
|
+
'spree_adjustments.amount as sales_tax',
|
57
|
+
'spree_zones.id as zone_id',
|
58
|
+
'spree_zones.name as zone_name',
|
59
|
+
*time_scale_selects('spree_adjustments')
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Spree::SalesTaxReport::MonthlySalesTaxComparisonChart
|
2
|
+
def initialize(result)
|
3
|
+
@time_dimension = result.time_dimension
|
4
|
+
@grouped_by_zone_name = result.observations.group_by(&:zone_name)
|
5
|
+
@time_series = []
|
6
|
+
if @grouped_by_zone_name.first.present?
|
7
|
+
@time_series = @grouped_by_zone_name.values.first.collect { |observation| observation.send(@time_dimension) }
|
8
|
+
end
|
9
|
+
@chart_series = @grouped_by_zone_name.map { |zone_name, observations| { type: 'column', name: zone_name, data: observations.collect(&:sales_tax) } }
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
{
|
14
|
+
id: 'sale-tax',
|
15
|
+
json: {
|
16
|
+
chart: { type: 'column' },
|
17
|
+
title: {
|
18
|
+
useHTML: true,
|
19
|
+
text: "<span class='chart-title'>Monthly Sales Tax Comparison</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Compare the Sales tax collected from different Zones'></span>"
|
20
|
+
},
|
21
|
+
xAxis: { categories: @time_series },
|
22
|
+
yAxis: {
|
23
|
+
title: { text: 'Value($)' }
|
24
|
+
},
|
25
|
+
tooltip: { valuePrefix: '$' },
|
26
|
+
legend: {
|
27
|
+
layout: 'vertical',
|
28
|
+
align: 'right',
|
29
|
+
verticalAlign: 'middle',
|
30
|
+
borderWidth: 0
|
31
|
+
},
|
32
|
+
series: @chart_series
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Spree
|
2
|
+
class ShippingCostReport < Spree::Report
|
3
|
+
HEADERS = { name: :string, shipping_charge: :integer, revenue: :integer, shipping_cost_percentage: :integer }
|
4
|
+
SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date }
|
5
|
+
SORTABLE_ATTRIBUTES = []
|
6
|
+
|
7
|
+
class Result < Spree::Report::TimedResult
|
8
|
+
charts ShippingCostDistributionChart
|
9
|
+
|
10
|
+
def build_empty_observations
|
11
|
+
super
|
12
|
+
@_shipping_methods = @results.collect { |r| r['name'] }.uniq
|
13
|
+
@observations = @_shipping_methods.collect do |shipping_method|
|
14
|
+
@observations.collect do |observation|
|
15
|
+
_d_observation = observation.dup
|
16
|
+
_d_observation.name = shipping_method
|
17
|
+
_d_observation.revenue = 0
|
18
|
+
_d_observation.shipping_charge = 0
|
19
|
+
_d_observation.shipping_cost_percentage = 0
|
20
|
+
_d_observation
|
21
|
+
end
|
22
|
+
end.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
class Observation < Spree::Report::TimedObservation
|
26
|
+
observation_fields [:name, :shipping_charge, :revenue, :shipping_cost_percentage]
|
27
|
+
|
28
|
+
def describes?(result, time_scale)
|
29
|
+
(name = result['name']) && super
|
30
|
+
end
|
31
|
+
|
32
|
+
def shipping_cost_percentage
|
33
|
+
((@shipping_charge.to_f * 100) / @revenue.to_f).round(2)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def report_query
|
39
|
+
ar_shipping_methods = Arel::Table.new(:spree_shipping_methods)
|
40
|
+
ar_subquery_with_rates = Arel::Table.new(:shipment_with_rates)
|
41
|
+
|
42
|
+
Spree::Report::QueryFragments
|
43
|
+
.from_subquery(shipment_with_rates, as: 'shipment_with_rates')
|
44
|
+
.join(ar_shipping_methods)
|
45
|
+
.on(ar_shipping_methods[:id].eq(ar_subquery_with_rates[:shipping_method_id]))
|
46
|
+
.project(
|
47
|
+
*time_scale_columns,
|
48
|
+
ar_shipping_methods[:id],
|
49
|
+
'revenue',
|
50
|
+
'shipping_charge',
|
51
|
+
'shipping_method_id',
|
52
|
+
'name'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
private def order_with_shipments
|
57
|
+
Spree::Order
|
58
|
+
.where.not(completed_at: nil)
|
59
|
+
.where(completed_at: reporting_period)
|
60
|
+
.joins(:shipments)
|
61
|
+
.select(
|
62
|
+
'spree_shipments.id as shipment_id',
|
63
|
+
'spree_orders.shipment_total as shipping_charge',
|
64
|
+
'spree_orders.id as order_id',
|
65
|
+
'spree_orders.total as order_total',
|
66
|
+
*time_scale_selects('spree_orders')
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
private def shipment_with_rates
|
71
|
+
ar_shipping_rates = Arel::Table.new(:spree_shipping_rates)
|
72
|
+
ar_subquery = Arel::Table.new(:results)
|
73
|
+
|
74
|
+
Spree::Report::QueryFragments.from_subquery(order_with_shipments)
|
75
|
+
.join(ar_shipping_rates)
|
76
|
+
.on(ar_shipping_rates[:shipment_id].eq(ar_subquery[:shipment_id]))
|
77
|
+
.where(ar_shipping_rates[:selected].eq(Arel::Nodes::Quoted.new(true)))
|
78
|
+
.group(*time_scale_columns, :shipping_method_id)
|
79
|
+
.order(*time_scale_columns)
|
80
|
+
.project(
|
81
|
+
*time_scale_columns,
|
82
|
+
'shipping_method_id',
|
83
|
+
'SUM(shipping_charge) as shipping_charge',
|
84
|
+
'SUM(order_total) as revenue'
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Spree::ShippingCostReport::ShippingCostDistributionChart
|
2
|
+
|
3
|
+
|
4
|
+
def initialize(result)
|
5
|
+
time_dimension = result.time_dimension
|
6
|
+
@grouped_by_shipping_method = result.observations.group_by(&:name)
|
7
|
+
@time_series = []
|
8
|
+
@time_series = @grouped_by_shipping_method.values.first.collect { |observation_value| observation_value.send(time_dimension) } if @grouped_by_shipping_method.first.present?
|
9
|
+
@result_series = @grouped_by_shipping_method.collect { |name, observations| { name: name, data: observations.collect(&:shipping_cost_percentage) } }
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
{
|
14
|
+
id: 'shipping-cost-percentage-comparison',
|
15
|
+
json: {
|
16
|
+
chart: { type: 'spline' },
|
17
|
+
title: {
|
18
|
+
useHTML: true,
|
19
|
+
text: "<span class='chart-title'>Monthly Shipping Comparison</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Compare the Shipping percentage (calculated on Revenue) among various shipment methods such as UPS, FedEx etc.'></span>"
|
20
|
+
},
|
21
|
+
xAxis: { categories: @time_series },
|
22
|
+
yAxis: {
|
23
|
+
title: { text: 'Percentage(%)' }
|
24
|
+
},
|
25
|
+
tooltip: { valueSuffix: '%' },
|
26
|
+
legend: {
|
27
|
+
layout: 'vertical',
|
28
|
+
align: 'right',
|
29
|
+
verticalAlign: 'middle',
|
30
|
+
borderWidth: 0
|
31
|
+
},
|
32
|
+
series: @result_series
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Spree
|
2
|
+
class TrendingSearchReport < Spree::Report
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :occurrences
|
4
|
+
HEADERS = { searched_term: :string, occurrences: :integer }
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, keywords_cont: :keyword }
|
6
|
+
SORTABLE_ATTRIBUTES = [:occurrences]
|
7
|
+
|
8
|
+
def paginated?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
class Result < Spree::Report::Result
|
13
|
+
charts FrequencyDistributionPieChart
|
14
|
+
|
15
|
+
class Observation < Spree::Report::Observation
|
16
|
+
observation_fields [:searched_term, :occurrences]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
deeplink searched_term: { template: %Q{<a href='/products?utf8=%E2%9C%93&keywords={%# o['searched_term'] %}' target="_blank">{%# o['searched_term'] %}</a>} }
|
21
|
+
|
22
|
+
def paginated_report_query
|
23
|
+
report_query
|
24
|
+
.take(records_per_page)
|
25
|
+
.skip(current_page)
|
26
|
+
end
|
27
|
+
|
28
|
+
def record_count_query
|
29
|
+
Spree::Report::QueryFragments.from_subquery(report_query).project(Arel.star.count)
|
30
|
+
end
|
31
|
+
|
32
|
+
def report_query
|
33
|
+
Spree::Report::QueryFragments.from_subquery(searches)
|
34
|
+
.project("count(searched_term) as occurrences", "searched_term")
|
35
|
+
.group("searched_term")
|
36
|
+
end
|
37
|
+
|
38
|
+
private def searches
|
39
|
+
Spree::PageEvent
|
40
|
+
.where(activity: 'search')
|
41
|
+
.where(created_at: reporting_period)
|
42
|
+
.where(Spree::PageEvent.arel_table[:search_keywords].matches(keyword_search))
|
43
|
+
.select("search_keywords as searched_term")
|
44
|
+
end
|
45
|
+
|
46
|
+
private def keyword_search
|
47
|
+
search[:keywords_cont].present? ? "%#{ search[:keywords_cont] }%" : '%'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Spree::TrendingSearchReport::FrequencyDistributionPieChart
|
2
|
+
attr_accessor :chart_data
|
3
|
+
|
4
|
+
def initialize(result)
|
5
|
+
total_occurrences = result.observations.sum(&:occurrences).to_f
|
6
|
+
self.chart_data = result.observations.collect { |x| { name: x.searched_term, y: x.occurrences/total_occurrences } }
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_h
|
10
|
+
{
|
11
|
+
|
12
|
+
name: 'trending-search',
|
13
|
+
json: {
|
14
|
+
chart: { type: 'pie' },
|
15
|
+
title: {
|
16
|
+
useHTML: true,
|
17
|
+
text: "<span class='chart-title'>Trending Search Keywords(Top 20)</span><span class='fa fa-question-circle' data-toggle='tooltip' title='Track the most trending keywords searched by users'></span>"
|
18
|
+
},
|
19
|
+
tooltip: {
|
20
|
+
pointFormat: 'Search %: <b>{point.percentage:.1f}%</b>'
|
21
|
+
},
|
22
|
+
plotOptions: {
|
23
|
+
pie: {
|
24
|
+
allowPointSelect: true,
|
25
|
+
cursor: 'pointer',
|
26
|
+
dataLabels: {
|
27
|
+
enabled: false
|
28
|
+
},
|
29
|
+
showInLegend: true
|
30
|
+
}
|
31
|
+
},
|
32
|
+
series: [
|
33
|
+
{
|
34
|
+
name: 'Hits',
|
35
|
+
data: chart_data
|
36
|
+
}
|
37
|
+
]
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|