From aecd0c6e8c35243429465394fa40d81847375653 Mon Sep 17 00:00:00 2001 From: Tanmay Sinha Date: Mon, 27 Jun 2016 10:55:04 +0530 Subject: [PATCH 01/12] Bump Spree version to 3.2.0.alpha --- Gemfile | 4 ++-- spree_reportify.gemspec | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 32d3ecd..d58e0c1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'spree', github: 'spree/spree', branch: '3-0-stable' -gem 'spree_events_tracker', git: "https://github.com/vinsol/spree_events_tracker.git", branch: 'add-tracker' +gem 'spree', github: 'spree/spree', branch: 'master' +gem 'spree_events_tracker', git: "https://github.com/vinsol/spree_events_tracker.git", branch: '3-2-alpha' gemspec diff --git a/spree_reportify.gemspec b/spree_reportify.gemspec index fd975b6..28d8ad5 100644 --- a/spree_reportify.gemspec +++ b/spree_reportify.gemspec @@ -1,7 +1,7 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = 'spree_reportify' - s.version = '3.0.7' + s.version = '3.2.0.alpha' s.summary = 'Add gem summary here' s.description = 'Add (optional) gem description here' s.required_ruby_version = '>= 2.1.0' @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - spree_version = '~> 3.0.7' + spree_version = '~> 3.2.0.alpha' s.add_dependency 'spree_core', spree_version @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'coffee-rails', '~> 4.0.0' s.add_development_dependency 'database_cleaner', '~> 1.2.0' s.add_development_dependency 'factory_girl', '~> 4.5' - s.add_development_dependency 'ffaker', '~> 1.32.0' + s.add_development_dependency 'ffaker', '~> 2.2.0' s.add_development_dependency 'mysql2', '~> 0.4.0' s.add_development_dependency 'pg', '~> 0.18.0' s.add_development_dependency 'rspec-rails', '~> 3.1' From 51d8a1a61af589c2e4abdfaea8bd49c65965ce61 Mon Sep 17 00:00:00 2001 From: Tanmay Sinha Date: Mon, 27 Jun 2016 11:44:35 +0530 Subject: [PATCH 02/12] Use spree_events_tracker master branch for spree 3.2.0.alpha --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index d58e0c1..922dd87 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' gem 'spree', github: 'spree/spree', branch: 'master' -gem 'spree_events_tracker', git: "https://github.com/vinsol/spree_events_tracker.git", branch: '3-2-alpha' +gem 'spree_events_tracker', git: "https://github.com/vinsol/spree_events_tracker.git", branch: 'master' gemspec From e4f0b882be5c082ab9a3016f03e43aabf115cae2 Mon Sep 17 00:00:00 2001 From: Sawan Gupta Date: Wed, 30 Nov 2016 17:01:22 +0530 Subject: [PATCH 03/12] Update gem name from spree_reportify to spree_admin_insights --- README.md | 64 +++++++++++++------ Rakefile | 2 +- .../spree/backend/spree_admin_insights.js | 2 + .../paginator.js | 0 .../report_loader.js | 6 +- .../searcher.js | 2 +- .../table_sorter.js | 0 .../spree/backend/spree_reportify.js | 2 - ...e_reportify.js => spree_admin_insights.js} | 0 ...reportify.css => spree_admin_insights.css} | 0 ...reportify.css => spree_admin_insights.css} | 0 .../spree/best_selling_products_report.rb | 2 +- app/reports/spree/cart_additions_report.rb | 2 +- app/reports/spree/cart_removals_report.rb | 2 +- app/reports/spree/cart_updations_report.rb | 2 +- ...hod_transactions_conversion_rate_report.rb | 4 +- .../payment_method_transactions_report.rb | 4 +- app/reports/spree/product_views_report.rb | 4 +- .../product_views_to_cart_additions_report.rb | 6 +- .../product_views_to_purchases_report.rb | 4 +- app/reports/spree/promotional_cost_report.rb | 4 +- app/reports/spree/returned_products_report.rb | 2 +- app/reports/spree/sales_performance_report.rb | 10 +-- app/reports/spree/sales_tax_report.rb | 4 +- app/reports/spree/shipping_cost_report.rb | 8 +-- app/reports/spree/trending_search_report.rb | 6 +- app/reports/spree/unique_purchases_report.rb | 2 +- app/reports/spree/user_pool_report.rb | 10 +-- .../spree/users_not_converted_report.rb | 2 +- ..._who_have_not_recently_purchased_report.rb | 4 +- .../users_who_recently_purchased_report.rb | 4 +- bin/rails | 2 +- config/initializers/assets.rb | 2 +- .../install/install_generator.rb | 12 ++-- lib/spree_admin_insights.rb | 2 + .../engine.rb | 6 +- .../factories.rb | 2 +- lib/spree_reportify.rb | 2 - spec/spec_helper.rb | 4 +- ...fy.gemspec => spree_admin_insights.gemspec | 8 +-- 40 files changed, 116 insertions(+), 88 deletions(-) create mode 100644 app/assets/javascripts/spree/backend/spree_admin_insights.js rename app/assets/javascripts/spree/backend/{spree_reportify => spree_admin_insights}/paginator.js (100%) rename app/assets/javascripts/spree/backend/{spree_reportify => spree_admin_insights}/report_loader.js (97%) rename app/assets/javascripts/spree/backend/{spree_reportify => spree_admin_insights}/searcher.js (98%) rename app/assets/javascripts/spree/backend/{spree_reportify => spree_admin_insights}/table_sorter.js (100%) delete mode 100644 app/assets/javascripts/spree/backend/spree_reportify.js rename app/assets/javascripts/spree/frontend/{spree_reportify.js => spree_admin_insights.js} (100%) rename app/assets/stylesheets/spree/backend/{spree_reportify.css => spree_admin_insights.css} (100%) rename app/assets/stylesheets/spree/frontend/{spree_reportify.css => spree_admin_insights.css} (100%) rename lib/generators/{spree_reportify => spree_admin_insights}/install/install_generator.rb (71%) create mode 100644 lib/spree_admin_insights.rb rename lib/{spree_reportify => spree_admin_insights}/engine.rb (77%) rename lib/{spree_reportify => spree_admin_insights}/factories.rb (84%) delete mode 100644 lib/spree_reportify.rb rename spree_reportify.gemspec => spree_admin_insights.gemspec (90%) diff --git a/README.md b/README.md index 7eec37a..12e4adf 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,53 @@ -SpreeReportify +[SpreeAdminInsights](http://vinsol.com/spreecommerce-admin-insights) ============== -Introduction goes here. +When it comes to driving an Ecommerce business, knowing the right metrics and access to relevant data is half the battle won! This allows you to take immediate and more importantly, the right action. + +This extension provides extensive and targeted reports for the Admin. Which products were viewed the most yesterday, which brand is most popular in a particular geography, which user is a consistent buyer and much more, all the reports a website owner could probably need are a click away! + +Dependency +--------- +you need to install [spree_events_tracker](https://github.com/vinsol-spree-contrib/spree_events_tracker) gem. + +Features +-------- +Elaborate reporting from the following categories are available: +* Financial Analysis - Involves reports around sales, payment methods and shipping etc +* Product Analysis - Insights of product purchase, abandoned cart etc +* Promotional analysis - Reports of promotional costs etc are available. +* Search Analysis - Search details reports. +* User Analysis - Includes elaborate user analysis. + +**Other features include :** +* Search and Filter +* Save reports in various formats. +* Refresh reports +* Reset report +* Remove pagination or change pagination count. Installation ------------ -Add spree_reportify to your Gemfile: +1. Add spree_admin_insights to your Gemfile: -```ruby -gem 'spree_reportify' -``` + ```ruby + gem 'spree_admin_insights', git: 'https://github.com/vinsol-spree-contrib/spree-admin-insights' + ``` -Bundle your dependencies and run the installation generator: +2. Bundle your dependencies and run the installation generator: -```shell -bundle -bundle exec rails g spree_reportify:install -``` + ```shell + bundle + bundle exec rails g spree_admin_insights:install + ``` + +3. Restart your server + +Usage +------- +Once installed it will automatically starts all data loging and statistical analysis and provides you a user friendly graphical representation of reports. This extension also allows you to download the reports in multiple formats. For more detailed usage please see [this](http://vinsol.com/spreecommerce-admin-insights) blog. + +To access these reports goto admin section and click on 'Insights' section in the vertical menu bar. Testing ------- @@ -26,14 +56,12 @@ First bundle your dependencies, then run `rake`. `rake` will default to building ```shell bundle -bundle exec rake +bundle exec rspec spec ``` -When testing your applications integration with this extension you may use it's factories. -Simply add this require statement to your spec_helper: +Credits +------- -```ruby -require 'spree_reportify/factories' -``` +[![vinsol.com: Ruby on Rails, iOS and Android developers](http://vinsol.com/vin_logo.png "Ruby on Rails, iOS and Android developers")](http://vinsol.com) -Copyright (c) 2016 [name of extension creator], released under the New BSD License +Copyright (c) 2016 [vinsol.com](http://vinsol.com "Ruby on Rails, iOS and Android developers"), released under the New MIT License diff --git a/Rakefile b/Rakefile index b6da392..4a5707e 100644 --- a/Rakefile +++ b/Rakefile @@ -16,6 +16,6 @@ end desc 'Generates a dummy app for testing' task :test_app do - ENV['LIB_NAME'] = 'spree_reportify' + ENV['LIB_NAME'] = 'spree_admin_insights' Rake::Task['extension:test_app'].invoke end diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights.js b/app/assets/javascripts/spree/backend/spree_admin_insights.js new file mode 100644 index 0000000..4bca314 --- /dev/null +++ b/app/assets/javascripts/spree/backend/spree_admin_insights.js @@ -0,0 +1,2 @@ +//= require spree/backend/spree_admin_insights/report_loader +//= require spree/backend/tmpl diff --git a/app/assets/javascripts/spree/backend/spree_reportify/paginator.js b/app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js similarity index 100% rename from app/assets/javascripts/spree/backend/spree_reportify/paginator.js rename to app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js diff --git a/app/assets/javascripts/spree/backend/spree_reportify/report_loader.js b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js similarity index 97% rename from app/assets/javascripts/spree/backend/spree_reportify/report_loader.js rename to app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js index d8ebb11..520e2a6 100644 --- a/app/assets/javascripts/spree/backend/spree_reportify/report_loader.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js @@ -1,6 +1,6 @@ -//= require spree/backend/spree_reportify/paginator -//= require spree/backend/spree_reportify/searcher -//= require spree/backend/spree_reportify/table_sorter +//= require spree/backend/spree_admin_insights/paginator +//= require spree/backend/spree_admin_insights/searcher +//= require spree/backend/spree_admin_insights/table_sorter function ReportLoader(inputs) { this.$selectList = inputs.reportsSelectBox; diff --git a/app/assets/javascripts/spree/backend/spree_reportify/searcher.js b/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js similarity index 98% rename from app/assets/javascripts/spree/backend/spree_reportify/searcher.js rename to app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js index 08554f3..282bf6a 100644 --- a/app/assets/javascripts/spree/backend/spree_reportify/searcher.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js @@ -1,4 +1,4 @@ -//= require spree/backend/spree_reportify/paginator +//= require spree/backend/spree_admin_insights/paginator function Searcher(inputs, reportLoader) { this.$insightsTableList = inputs.insightsDiv; diff --git a/app/assets/javascripts/spree/backend/spree_reportify/table_sorter.js b/app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js similarity index 100% rename from app/assets/javascripts/spree/backend/spree_reportify/table_sorter.js rename to app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js diff --git a/app/assets/javascripts/spree/backend/spree_reportify.js b/app/assets/javascripts/spree/backend/spree_reportify.js deleted file mode 100644 index 916b945..0000000 --- a/app/assets/javascripts/spree/backend/spree_reportify.js +++ /dev/null @@ -1,2 +0,0 @@ -//= require spree/backend/spree_reportify/report_loader -//= require spree/backend/tmpl diff --git a/app/assets/javascripts/spree/frontend/spree_reportify.js b/app/assets/javascripts/spree/frontend/spree_admin_insights.js similarity index 100% rename from app/assets/javascripts/spree/frontend/spree_reportify.js rename to app/assets/javascripts/spree/frontend/spree_admin_insights.js diff --git a/app/assets/stylesheets/spree/backend/spree_reportify.css b/app/assets/stylesheets/spree/backend/spree_admin_insights.css similarity index 100% rename from app/assets/stylesheets/spree/backend/spree_reportify.css rename to app/assets/stylesheets/spree/backend/spree_admin_insights.css diff --git a/app/assets/stylesheets/spree/frontend/spree_reportify.css b/app/assets/stylesheets/spree/frontend/spree_admin_insights.css similarity index 100% rename from app/assets/stylesheets/spree/frontend/spree_reportify.css rename to app/assets/stylesheets/spree/frontend/spree_admin_insights.css diff --git a/app/reports/spree/best_selling_products_report.rb b/app/reports/spree/best_selling_products_report.rb index 2e3ecd7..d93e8c3 100644 --- a/app/reports/spree/best_selling_products_report.rb +++ b/app/reports/spree/best_selling_products_report.rb @@ -13,7 +13,7 @@ def initialize(options) end def generate - ::SpreeReportify::ReportDb[:spree_line_items___line_items]. + ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. join(:spree_orders___orders, id: :order_id). join(:spree_variants___variants, variants__id: :line_items__variant_id). join(:spree_products___products, products__id: :variants__product_id). diff --git a/app/reports/spree/cart_additions_report.rb b/app/reports/spree/cart_additions_report.rb index f92e99c..6551278 100644 --- a/app/reports/spree/cart_additions_report.rb +++ b/app/reports/spree/cart_additions_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate - SpreeReportify::ReportDb[:spree_cart_events___cart_events]. + SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. join(:spree_variants___variants, id: :variant_id). join(:spree_products___products, id: :product_id). where(cart_events__activity: 'add'). diff --git a/app/reports/spree/cart_removals_report.rb b/app/reports/spree/cart_removals_report.rb index 1b86638..10d008b 100644 --- a/app/reports/spree/cart_removals_report.rb +++ b/app/reports/spree/cart_removals_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate - SpreeReportify::ReportDb[:spree_cart_events___cart_events]. + SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. join(:spree_variants___variants, id: :variant_id). join(:spree_products___products, id: :product_id). where(cart_events__activity: 'remove'). diff --git a/app/reports/spree/cart_updations_report.rb b/app/reports/spree/cart_updations_report.rb index a14af9a..be3aad7 100644 --- a/app/reports/spree/cart_updations_report.rb +++ b/app/reports/spree/cart_updations_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate - SpreeReportify::ReportDb[:spree_cart_events___cart_events]. + SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. join(:spree_variants___variants, id: :variant_id). join(:spree_products___products, id: :product_id). where(activity: 'update'). diff --git a/app/reports/spree/payment_method_transactions_conversion_rate_report.rb b/app/reports/spree/payment_method_transactions_conversion_rate_report.rb index 96f116c..cb5040f 100644 --- a/app/reports/spree/payment_method_transactions_conversion_rate_report.rb +++ b/app/reports/spree/payment_method_transactions_conversion_rate_report.rb @@ -10,7 +10,7 @@ def no_pagination? end def generate - payment_methods = SpreeReportify::ReportDb[:spree_payment_methods___payment_methods]. + payment_methods = SpreeAdminInsights::ReportDb[:spree_payment_methods___payment_methods]. join(:spree_payments___payments, payment_method_id: :id). where(payments__created_at: @start_date..@end_date). #filter by params select{[ @@ -22,7 +22,7 @@ def generate Sequel.as(YEAR(:payments__created_at), :year) ]} - group_by_months = SpreeReportify::ReportDb[payment_methods]. + group_by_months = SpreeAdminInsights::ReportDb[payment_methods]. group(:months_name, :payment_method_name, :payment_state). order(:year, :number). select{[ diff --git a/app/reports/spree/payment_method_transactions_report.rb b/app/reports/spree/payment_method_transactions_report.rb index c02498a..fcd1234 100644 --- a/app/reports/spree/payment_method_transactions_report.rb +++ b/app/reports/spree/payment_method_transactions_report.rb @@ -10,7 +10,7 @@ def no_pagination? end def generate - payments = SpreeReportify::ReportDb[:spree_payment_methods___payment_methods]. + payments = SpreeAdminInsights::ReportDb[:spree_payment_methods___payment_methods]. join(:spree_payments___payments, payment_method_id: :id). where(payments__created_at: @start_date..@end_date). #filter by params select{[ @@ -21,7 +21,7 @@ def generate Sequel.as(YEAR(:payments__created_at), :year) ]} - group_by_months = SpreeReportify::ReportDb[payments]. + group_by_months = SpreeAdminInsights::ReportDb[payments]. group(:months_name, :payment_method_name). order(:year, :number). select{[ diff --git a/app/reports/spree/product_views_report.rb b/app/reports/spree/product_views_report.rb index e86f572..5b72a61 100644 --- a/app/reports/spree/product_views_report.rb +++ b/app/reports/spree/product_views_report.rb @@ -12,7 +12,7 @@ def initialize(options) end def generate - unique_session_results = ::SpreeReportify::ReportDb[:spree_products___products]. + unique_session_results = ::SpreeAdminInsights::ReportDb[:spree_products___products]. join(:spree_page_events___page_events, target_id: :id). where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). where(page_events__created_at: @start_date..@end_date).where(Sequel.ilike(:products__name, @name)). @@ -24,7 +24,7 @@ def generate page_events__actor_id.as(actor_id) ]}.as(:unique_session_results) - ::SpreeReportify::ReportDb[unique_session_results]. + ::SpreeAdminInsights::ReportDb[unique_session_results]. group(:product_name). order(sortable_sequel_expression) end diff --git a/app/reports/spree/product_views_to_cart_additions_report.rb b/app/reports/spree/product_views_to_cart_additions_report.rb index fef4192..72c108a 100644 --- a/app/reports/spree/product_views_to_cart_additions_report.rb +++ b/app/reports/spree/product_views_to_cart_additions_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate(options = {}) - cart_additions = SpreeReportify::ReportDb[:spree_cart_events___cart_events]. + cart_additions = SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. join(:spree_variants___variants, id: :variant_id). join(:spree_products___products, id: :product_id). where(cart_events__activity: 'add'). @@ -23,7 +23,7 @@ def generate(options = {}) ]}.as(:cart_additions) - total_views_results = ::SpreeReportify::ReportDb[:spree_products___products]. + total_views_results = ::SpreeAdminInsights::ReportDb[:spree_products___products]. join(:spree_page_events___page_events, target_id: :id). where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). group(:product_name). @@ -32,7 +32,7 @@ def generate(options = {}) count('*').as(views) ]} - ::SpreeReportify::ReportDb[total_views_results]. + ::SpreeAdminInsights::ReportDb[total_views_results]. join(cart_additions, product_name: :product_name). order(sortable_sequel_expression) end diff --git a/app/reports/spree/product_views_to_purchases_report.rb b/app/reports/spree/product_views_to_purchases_report.rb index f20cfa7..8dea56e 100644 --- a/app/reports/spree/product_views_to_purchases_report.rb +++ b/app/reports/spree/product_views_to_purchases_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate(options = {}) - line_items = ::SpreeReportify::ReportDb[:spree_line_items___line_items]. + line_items = ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. join(:spree_orders___orders, id: :order_id). join(:spree_variants___variants, variants__id: :line_items__variant_id). join(:spree_products___products, products__id: :variants__product_id). @@ -25,7 +25,7 @@ def generate(options = {}) products__id.as(product_id)]}. group(:products__name).as(:line_items) - ::SpreeReportify::ReportDb[line_items].join(:spree_page_events___page_events, page_events__target_id: :product_id). + ::SpreeAdminInsights::ReportDb[line_items].join(:spree_page_events___page_events, page_events__target_id: :product_id). where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). group(:product_name). order(sortable_sequel_expression) diff --git a/app/reports/spree/promotional_cost_report.rb b/app/reports/spree/promotional_cost_report.rb index 39ea325..ce42c02 100644 --- a/app/reports/spree/promotional_cost_report.rb +++ b/app/reports/spree/promotional_cost_report.rb @@ -15,7 +15,7 @@ def initialize(options) end def generate(options = {}) - adjustments_with_month_name = SpreeReportify::ReportDb[:spree_adjustments___adjustments]. + adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. join(:spree_promotion_actions___promotion_actions, id: :source_id). join(:spree_promotions___promotions, id: :promotion_id). where(adjustments__source_type: "Spree::PromotionAction"). @@ -32,7 +32,7 @@ def generate(options = {}) Sequel.as(MONTH(:adjustments__created_at), :number) ]} - group_by_months = SpreeReportify::ReportDb[adjustments_with_month_name]. + group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. group(:months_name, :promotions_id). order(:year, :number). select{[ diff --git a/app/reports/spree/returned_products_report.rb b/app/reports/spree/returned_products_report.rb index 71787ca..f8657ee 100644 --- a/app/reports/spree/returned_products_report.rb +++ b/app/reports/spree/returned_products_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate - SpreeReportify::ReportDb[:spree_return_authorizations]. + SpreeAdminInsights::ReportDb[:spree_return_authorizations]. join(:spree_return_items, return_authorization_id: :spree_return_authorizations__id). join(:spree_inventory_units, spree_inventory_units__id: :inventory_unit_id). join(:spree_variants, spree_variants__id: :variant_id). diff --git a/app/reports/spree/sales_performance_report.rb b/app/reports/spree/sales_performance_report.rb index f80bfdd..abf8b6c 100644 --- a/app/reports/spree/sales_performance_report.rb +++ b/app/reports/spree/sales_performance_report.rb @@ -9,7 +9,7 @@ def no_pagination? end def generate(options = {}) - order_join_line_item = SpreeReportify::ReportDb[:spree_orders___orders]. + order_join_line_item = SpreeAdminInsights::ReportDb[:spree_orders___orders]. exclude(completed_at: nil). where(orders__created_at: @start_date..@end_date). #filter by params join(:spree_line_items___line_items, order_id: :id). @@ -23,7 +23,7 @@ def generate(options = {}) Sequel.as(YEAR(:orders__created_at), :year) ]} - group_by_months = SpreeReportify::ReportDb[order_join_line_item]. + group_by_months = SpreeAdminInsights::ReportDb[order_join_line_item]. group(:months_name). order(:year, :number). select{[ @@ -37,7 +37,7 @@ def generate(options = {}) Sequel.as(0, :promotion_discount) ]} - adjustments_with_month_name = SpreeReportify::ReportDb[:spree_adjustments___adjustments]. + adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. where(adjustments__source_type: "Spree::PromotionAction"). where(adjustments__created_at: @start_date..@end_date). #filter by params select{[ @@ -47,7 +47,7 @@ def generate(options = {}) Sequel.as(MONTH(:adjustments__created_at), :number) ]} - promotions_group_by_months = SpreeReportify::ReportDb[adjustments_with_month_name]. + promotions_group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. group(:months_name). order(:year, :number). select{[ @@ -61,7 +61,7 @@ def generate(options = {}) Sequel.as(SUM(promotion_discount), :promotion_discount) ]} - union_stats = SpreeReportify::ReportDb[group_by_months.union(promotions_group_by_months)]. + union_stats = SpreeAdminInsights::ReportDb[group_by_months.union(promotions_group_by_months)]. group(:months_name). order(:year, :number). select{[ diff --git a/app/reports/spree/sales_tax_report.rb b/app/reports/spree/sales_tax_report.rb index ec85dc0..21a8e56 100644 --- a/app/reports/spree/sales_tax_report.rb +++ b/app/reports/spree/sales_tax_report.rb @@ -9,7 +9,7 @@ def no_pagination? end def generate(options = {}) - adjustments_with_month_name = SpreeReportify::ReportDb[:spree_adjustments___adjustments]. + adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. join(:spree_tax_rates___tax_rates, id: :source_id). join(:spree_zones___zones, id: :zone_id). where(adjustments__source_type: "Spree::TaxRate", adjustments__adjustable_type: "Spree::LineItem"). @@ -23,7 +23,7 @@ def generate(options = {}) Sequel.as(MONTH(:adjustments__created_at), :number) ]} - group_by_months = SpreeReportify::ReportDb[adjustments_with_month_name]. + group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. group(:months_name, :zone_id). order(:year, :number). select{[ diff --git a/app/reports/spree/shipping_cost_report.rb b/app/reports/spree/shipping_cost_report.rb index d1f8b4b..8edef5b 100644 --- a/app/reports/spree/shipping_cost_report.rb +++ b/app/reports/spree/shipping_cost_report.rb @@ -9,7 +9,7 @@ def no_pagination? end def generate(options = {}) - order_join_shipments = SpreeReportify::ReportDb[:spree_orders___orders]. + order_join_shipments = SpreeAdminInsights::ReportDb[:spree_orders___orders]. exclude(completed_at: nil). join(:spree_shipments___shipments, order_id: :id). where(orders__created_at: @start_date..@end_date). #filter by params @@ -23,7 +23,7 @@ def generate(options = {}) Sequel.as(YEAR(:orders__created_at), :year) ]}.as(:order_shipment) - order_shipment_join_shipment_rates = SpreeReportify::ReportDb[order_join_shipments]. + order_shipment_join_shipment_rates = SpreeAdminInsights::ReportDb[order_join_shipments]. join(:spree_shipping_rates___shipping_rates, shipment_id: :order_shipment__shipment_id). where(selected: true). select{[ @@ -37,7 +37,7 @@ def generate(options = {}) Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), ]}.as(:order_shipment_rates) - revenue_table = SpreeReportify::ReportDb[order_shipment_join_shipment_rates]. + revenue_table = SpreeAdminInsights::ReportDb[order_shipment_join_shipment_rates]. group(:months_name). select{[ Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), @@ -45,7 +45,7 @@ def generate(options = {}) order_id ]} - group_by_months = SpreeReportify::ReportDb[order_shipment_join_shipment_rates]. + group_by_months = SpreeAdminInsights::ReportDb[order_shipment_join_shipment_rates]. join(:spree_shipping_methods, id: :order_shipment_rates__shipping_method_id). join(revenue_table, months_name: :order_shipment_rates__months_name). group(:months_name, :spree_shipping_methods__id). diff --git a/app/reports/spree/trending_search_report.rb b/app/reports/spree/trending_search_report.rb index beabcf7..280bfb0 100644 --- a/app/reports/spree/trending_search_report.rb +++ b/app/reports/spree/trending_search_report.rb @@ -13,7 +13,7 @@ def initialize(options) end def generate(options = {}) - top_searches = SpreeReportify::ReportDb[:spree_page_events___page_events]. + top_searches = SpreeAdminInsights::ReportDb[:spree_page_events___page_events]. where(page_events__activity: 'search'). where(page_events__created_at: @start_date..@end_date).where(Sequel.ilike(:page_events__search_keywords, @search_keywords_cont)). #filter by params group(:searched_term). @@ -32,8 +32,8 @@ def select_columns(dataset) def chart_data top_searches = select_columns(generate) - total_occurrences = SpreeReportify::ReportDb[top_searches].sum(:occurrences) - SpreeReportify::ReportDb[top_searches]. + total_occurrences = SpreeAdminInsights::ReportDb[top_searches].sum(:occurrences) + SpreeAdminInsights::ReportDb[top_searches]. select{[ Sequel.as((occurrences / total_occurrences) * 100, :y), Sequel.as(searched_term, :name) diff --git a/app/reports/spree/unique_purchases_report.rb b/app/reports/spree/unique_purchases_report.rb index 3c8dfc1..11fcc0a 100644 --- a/app/reports/spree/unique_purchases_report.rb +++ b/app/reports/spree/unique_purchases_report.rb @@ -11,7 +11,7 @@ def initialize(options) end def generate(options = {}) - ::SpreeReportify::ReportDb[:spree_line_items___line_items]. + ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. join(:spree_orders___orders, id: :order_id). join(:spree_variants___variants, variants__id: :line_items__variant_id). join(:spree_products___products, products__id: :variants__product_id). diff --git a/app/reports/spree/user_pool_report.rb b/app/reports/spree/user_pool_report.rb index 7f3b48b..2063d77 100644 --- a/app/reports/spree/user_pool_report.rb +++ b/app/reports/spree/user_pool_report.rb @@ -11,7 +11,7 @@ def no_pagination? def generate(options = {}) # order of column is important when we take union of two tables - new_sign_ups = SpreeReportify::ReportDb[:spree_users___users]. + new_sign_ups = SpreeAdminInsights::ReportDb[:spree_users___users]. where(users__created_at: @start_date..@end_date). select{[ id.as(:user_id), @@ -20,7 +20,7 @@ def generate(options = {}) Sequel.as(MONTH(:users__created_at), :number) ]} - group_new_sign_ups_by_months = SpreeReportify::ReportDb[new_sign_ups]. + group_new_sign_ups_by_months = SpreeAdminInsights::ReportDb[new_sign_ups]. group(:months_name). order(:year, :number). select{[ @@ -32,7 +32,7 @@ def generate(options = {}) Sequel.as(IFNULL(COUNT(user_id), 0), :new_sign_ups) ]} - vistors = SpreeReportify::ReportDb[:spree_page_events___page_events]. + vistors = SpreeAdminInsights::ReportDb[:spree_page_events___page_events]. where(page_events__created_at: @start_date..@end_date). select{[ Sequel.as(YEAR(:page_events__created_at), :year), @@ -42,7 +42,7 @@ def generate(options = {}) Sequel.as(session_id, :session) ]} - visitors_by_months = SpreeReportify::ReportDb[vistors]. + visitors_by_months = SpreeAdminInsights::ReportDb[vistors]. group(:months_name). order(:year, :number). select{[ @@ -57,7 +57,7 @@ def generate(options = {}) union_of_stats = group_new_sign_ups_by_months.union(visitors_by_months) - union_stats = SpreeReportify::ReportDb[union_of_stats]. + union_stats = SpreeAdminInsights::ReportDb[union_of_stats]. group(:months_name). order(:year, :number). select{[ diff --git a/app/reports/spree/users_not_converted_report.rb b/app/reports/spree/users_not_converted_report.rb index c27c01e..cb836df 100644 --- a/app/reports/spree/users_not_converted_report.rb +++ b/app/reports/spree/users_not_converted_report.rb @@ -12,7 +12,7 @@ def initialize(options) end def generate(options = {}) - SpreeReportify::ReportDb[:spree_users___users]. + SpreeAdminInsights::ReportDb[:spree_users___users]. left_join(:spree_orders___orders, user_id: :id). where(orders__completed_at: nil, orders__number: nil). where(users__created_at: @start_date..@end_date).where(Sequel.ilike(:users__email, @email_cont)). #filter by params diff --git a/app/reports/spree/users_who_have_not_recently_purchased_report.rb b/app/reports/spree/users_who_have_not_recently_purchased_report.rb index bb3d2b4..ccd121a 100644 --- a/app/reports/spree/users_who_have_not_recently_purchased_report.rb +++ b/app/reports/spree/users_who_have_not_recently_purchased_report.rb @@ -12,7 +12,7 @@ def initialize(options) end def generate(options = {}) - all_orders_with_users = SpreeReportify::ReportDb[:spree_users___users]. + all_orders_with_users = SpreeAdminInsights::ReportDb[:spree_users___users]. left_join(:spree_orders___orders, user_id: :id). where(Sequel.~(orders__completed_at: nil), Sequel.~(orders__completed_at: @start_date..@end_date)). where(Sequel.ilike(:users__email, @email_cont)). @@ -23,7 +23,7 @@ def generate(options = {}) :orders__completed_at___last_purchase_date ).as(:all_orders_with_users) - SpreeReportify::ReportDb[all_orders_with_users]. + SpreeAdminInsights::ReportDb[all_orders_with_users]. group(:all_orders_with_users__user_email). order(sortable_sequel_expression) end diff --git a/app/reports/spree/users_who_recently_purchased_report.rb b/app/reports/spree/users_who_recently_purchased_report.rb index f85168a..eb72ac1 100644 --- a/app/reports/spree/users_who_recently_purchased_report.rb +++ b/app/reports/spree/users_who_recently_purchased_report.rb @@ -12,7 +12,7 @@ def initialize(options) end def generate(options = {}) - all_orders_with_users = SpreeReportify::ReportDb[:spree_users___users]. + all_orders_with_users = SpreeAdminInsights::ReportDb[:spree_users___users]. left_join(:spree_orders___orders, user_id: :id). where(orders__completed_at: @start_date..@end_date). where(Sequel.ilike(:users__email, @email_cont)). @@ -23,7 +23,7 @@ def generate(options = {}) :orders__completed_at___last_purchase_date, ).as(:all_orders_with_users) - SpreeReportify::ReportDb[all_orders_with_users]. + SpreeAdminInsights::ReportDb[all_orders_with_users]. group(:all_orders_with_users__user_email). order(sortable_sequel_expression) end diff --git a/bin/rails b/bin/rails index 8cc7406..041298f 100644 --- a/bin/rails +++ b/bin/rails @@ -1,7 +1,7 @@ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. ENGINE_ROOT = File.expand_path('../..', __FILE__) -ENGINE_PATH = File.expand_path('../../lib/spree_reportify/engine', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/spree_admin_insights/engine', __FILE__) require 'rails/all' require 'rails/engine/commands' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4cec5a3..6ac5d40 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1 +1 @@ -Rails.application.config.assets.precompile += %w( spree/backend/spree_reportify.js ) +Rails.application.config.assets.precompile += %w( spree/backend/spree_admin_insights.js ) diff --git a/lib/generators/spree_reportify/install/install_generator.rb b/lib/generators/spree_admin_insights/install/install_generator.rb similarity index 71% rename from lib/generators/spree_reportify/install/install_generator.rb rename to lib/generators/spree_admin_insights/install/install_generator.rb index 4746759..38e220b 100644 --- a/lib/generators/spree_reportify/install/install_generator.rb +++ b/lib/generators/spree_admin_insights/install/install_generator.rb @@ -1,21 +1,21 @@ -module SpreeReportify +module SpreeAdminInsights module Generators class InstallGenerator < Rails::Generators::Base class_option :auto_run_migrations, :type => :boolean, :default => false def add_javascripts - append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_reportify\n" - append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_reportify\n" + append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_admin_insights\n" + append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_admin_insights\n" end def add_stylesheets - inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_reportify\n", :before => /\*\//, :verbose => true - inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_reportify\n", :before => /\*\//, :verbose => true + inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_admin_insights\n", :before => /\*\//, :verbose => true + inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_admin_insights\n", :before => /\*\//, :verbose => true end def add_migrations - run 'bundle exec rake railties:install:migrations FROM=spree_reportify' + run 'bundle exec rake railties:install:migrations FROM=spree_admin_insights' end def run_migrations diff --git a/lib/spree_admin_insights.rb b/lib/spree_admin_insights.rb new file mode 100644 index 0000000..c7af00d --- /dev/null +++ b/lib/spree_admin_insights.rb @@ -0,0 +1,2 @@ +require 'spree_core' +require 'spree_admin_insights/engine' diff --git a/lib/spree_reportify/engine.rb b/lib/spree_admin_insights/engine.rb similarity index 77% rename from lib/spree_reportify/engine.rb rename to lib/spree_admin_insights/engine.rb index 0c985d7..e69d9a8 100644 --- a/lib/spree_reportify/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -1,4 +1,4 @@ -module SpreeReportify +module SpreeAdminInsights class Engine < Rails::Engine require 'spree/core' require 'sequel' @@ -6,7 +6,7 @@ class Engine < Rails::Engine require 'csv' isolate_namespace Spree - engine_name 'spree_reportify' + engine_name 'spree_admin_insights' # use rspec for tests config.generators do |g| @@ -23,7 +23,7 @@ def self.activate config.after_initialize do # Connect to applications DB using ruby's Sequel wrapper - ::SpreeReportify::ReportDb = Sequel.connect(Rails.configuration.database_configuration[Rails.env]) + ::SpreeAdminInsights::ReportDb = Sequel.connect(Rails.configuration.database_configuration[Rails.env]) end end end diff --git a/lib/spree_reportify/factories.rb b/lib/spree_admin_insights/factories.rb similarity index 84% rename from lib/spree_reportify/factories.rb rename to lib/spree_admin_insights/factories.rb index 114b872..8b04e88 100644 --- a/lib/spree_reportify/factories.rb +++ b/lib/spree_admin_insights/factories.rb @@ -2,5 +2,5 @@ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them. # # Example adding this to your spec_helper will load these Factories for use: - # require 'spree_reportify/factories' + # require 'spree_admin_insights/factories' end diff --git a/lib/spree_reportify.rb b/lib/spree_reportify.rb deleted file mode 100644 index c9e85da..0000000 --- a/lib/spree_reportify.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'spree_core' -require 'spree_reportify/engine' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 78d2ba8..0de8cdf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -30,8 +30,8 @@ require 'spree/testing_support/factories' require 'spree/testing_support/url_helpers' -# Requires factories defined in lib/spree_reportify/factories.rb -require 'spree_reportify/factories' +# Requires factories defined in lib/spree_admin_insights/factories.rb +require 'spree_admin_insights/factories' RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods diff --git a/spree_reportify.gemspec b/spree_admin_insights.gemspec similarity index 90% rename from spree_reportify.gemspec rename to spree_admin_insights.gemspec index 28d8ad5..36728ab 100644 --- a/spree_reportify.gemspec +++ b/spree_admin_insights.gemspec @@ -1,14 +1,14 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY - s.name = 'spree_reportify' + s.name = 'spree_admin_insights' s.version = '3.2.0.alpha' s.summary = 'Add gem summary here' s.description = 'Add (optional) gem description here' s.required_ruby_version = '>= 2.1.0' - s.author = 'You' - s.email = 'you@example.com' - # s.homepage = 'http://www.spreecommerce.com' + s.author = 'Vinsol' + s.email = 'info@vinsol.com' + s.homepage = 'http://vinsol.com/spreecommerce-admin-insights' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") From 763d3c5bffb2019a576e0a26def625918f60210d Mon Sep 17 00:00:00 2001 From: nimish13 Date: Thu, 8 Dec 2016 16:27:11 +0530 Subject: [PATCH 04/12] show insights tab only if accessible --- .../admin/shared/_insights_side_menu.html.erb | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/app/views/spree/admin/shared/_insights_side_menu.html.erb b/app/views/spree/admin/shared/_insights_side_menu.html.erb index 578b84c..be189a5 100644 --- a/app/views/spree/admin/shared/_insights_side_menu.html.erb +++ b/app/views/spree/admin/shared/_insights_side_menu.html.erb @@ -1,14 +1,16 @@ - +<% if can? :admin, Spree::Admin::InsightsController %> + +<% end %> From 08ccc4036e6c2c467a880226163701cb87785132 Mon Sep 17 00:00:00 2001 From: Nemo Fang Date: Fri, 3 Feb 2017 11:22:32 +1300 Subject: [PATCH 05/12] Get db config from ActiveRecord::Base. --- lib/spree_admin_insights/engine.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/spree_admin_insights/engine.rb b/lib/spree_admin_insights/engine.rb index e69d9a8..09797aa 100644 --- a/lib/spree_admin_insights/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -23,7 +23,9 @@ def self.activate config.after_initialize do # Connect to applications DB using ruby's Sequel wrapper - ::SpreeAdminInsights::ReportDb = Sequel.connect(Rails.configuration.database_configuration[Rails.env]) + db_config = (ActiveRecord::Base.configurations[Rails.env] || + Rails.application.config.database_configuration[Rails.env]) + ::SpreeAdminInsights::ReportDb = Sequel.connect(db_config) end end end From 7fbcd671eac8b8d65ab30663e62a0822a4fc686b Mon Sep 17 00:00:00 2001 From: Dorian Sawa Date: Fri, 7 Apr 2017 11:06:29 +0200 Subject: [PATCH 06/12] Add polish translations --- config/locales/en.yml | 4 ++ config/locales/pl.yml | 164 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 config/locales/pl.yml diff --git a/config/locales/en.yml b/config/locales/en.yml index 1658bb1..bb160b3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,6 +5,10 @@ en: promotion_analysis: Promotion Analysis trending_search_analysis: Trending Search Analysis user_analysis: User Analysis + user_pool: + new_sign_ups: New users + active_users: Active Users(Registered) + guest_users: Guests insights: Insights insight: heading: 'Insights: %{type}' diff --git a/config/locales/pl.yml b/config/locales/pl.yml new file mode 100644 index 0000000..85a0dbc --- /dev/null +++ b/config/locales/pl.yml @@ -0,0 +1,164 @@ +pl: + spree: + finance_analysis: Analiza finansowa + product_analysis: Analiza produktowa + promotion_analysis: Analiza promocji + trending_search_analysis: Analiza wyszukiwania + user_analysis: Analiza użytkowników + user_pool: + new_sign_ups: Nowi użytkownicy + active_users: Zarejestrowani użytkownicy + guest_users: Goście + insights: Analityka + insight: + heading: 'Analiza: %{type}' + tab_heading: Analityka + not_found: Nie znaleziono raportu. + product_views: + title: Wyświetlenia produktu + description: Pokazuje liczbę wyświetleń produktu (strony produktu) przez klientów. + product_name: Produkt + views: Suma wyświetleń + users: Użytkownicy (Unikalni) + guest_sessions: Sesje gości + cart_additions: + title: Dodanie produktu do koszyka + additions: Dodatki + description: Wyświetla informację o tym jak wiele razy produkt został dodany do koszyka. + product_name: Produkt + quantity_change: Dodanie do koszyka (Liczba) + sku: Wariant + cart_removals: + title: Usunięcie produktu z koszyka + description: Wyświetla informację o tym jak wiele razy produkt został usunięty z koszyka. + product_name: Produkt + quantity_change: Usunięcie z koszyka (Liczba) + removals: Usunięcia + sku: Wariant + cart_updations: + title: Aktualizacja produktu + description: Wyświetla informację o tym jak wiele razy produkt został zaktualizowany z poziomu koszyka. + product_name: Produkt + quantity_decrease: Zmniejszenie liczby (Z koszyka) + quantity_increase: Zwiększenie liczby (Z koszyka) + removals: Usunięcie (Z koszyka) + sku: Wariant + updations: Suma aktualizacji + product_views_to_cart_additions: + title: Stosunek liczby wyświetleń produktu do liczby dodań do koszyka + description: Stosunek liczby wyświetleń produktu (strony produktu) do liczby dodań do koszyka tego produktu. + cart_additions: Konwersja (liczba produktu) + product_name: Produkt + views: Wyświetlenia produktu + cart_to_view_ratio: Stosunek dodań do koszyka + trending_search: + title: Wyszukiwania + description: Możesz zobacz jakie słowa kluczowe wpisują Twoi klienci w wyszukiwarkę. + searched_term: Słowa kluczowe + occurrences: Występowanie + users_not_converted: + title: Użytkownicy nieskonwertowani + description: Informacja o zarejestrowanych użytkownikach, którzy nie zakupili żadnego produktu. + signup_date: Zarejestrowany o + user_email: E-mail + users_who_recently_purchased: + title: Użytkownicy, którzy ostatnio dokonali zakupu + description: Liczba użytkowników, którzy ostatnio dokonali zakupu. + last_purchase_date: Czas ostatniego zakupu + last_purchased_order_number: Numer ostatniego zamówienia + purchase_count: Liczba dokonanych zakupów + user_email: E-mail + users_who_have_not_recently_purchased: + title: Użytkownicy, którzy nie dokonali ostatnio zakupów + description: Liczba użytkowników, którzy nie dokonali zakupów od dłuższego czasu + last_purchase_date: Czas ostatniego zakupu + last_purchased_order_number: Numer ostatniego zamówienia + user_email: E-mail + user_pool: + title: Pula użytkowników + description: Wyświetla miesięczną statystyke o użytkownikach odwiedzających stronę. + months_name: Miesiąc + guest_users: Goście + active_users: Zarejestrowani użytkownicy + new_sign_ups: Nowi użytkownicy + payment_method_transactions: + title: Liczba transakcji + description: Transakcje pochodzące z różnych metod płatności. + payment_method_name: Metoda płatności + payment_amount: Kwota transakcji + months_name: Miesiąc + payment_method_transactions_conversion_rate: + payment_method_name: Metoda płatności + title: Konwersja metod płatności + description: Stosunek transakcji zakończonych sukcesem do tych zakończonych niepowodzeniem. + months_name: Miesiąc + payment_state: Stan płatności + count: Liczba + promotional_cost: + title: Koszt promocyjny + description: Suma pieniędzy przeznaczonych na poszczególne promocje - Admin może zobaczyć sumę zainwestowanych pieniędzy, dochód i zysk z poszczególnej promocji. + promotion_code: Kod + promotion_discount: Zniżka + promotion_name: Promocja + promotion_start_date: Czas rozpoczęcia + promotion_end_date: Czas wygaśnięcia + usage_count: Wykorzystanie + months_name: Miesiąc + annual_promotional_cost: + title: Roczny koszt promocji + description: Całkowita suma pieniędzy zainwestowanych w poszczególne promocje - Admin może zobaczyć sumę zainwestowanych pieniędzy, dochód i zysk z poszczególnej promocji. + promotion_discount: Zniżka + promotion_name: Promocja + usage_count: Wykorzystanie + sales_performance: + title: Wyniki sprzedaży + description: Informacje o dochodach, podatkach, przesyłkach i zwrotach. + adjustment_total: Suma poprawek + cost_price: Cena kosztu + months_name: Miesiąc + profit_loss: Zysk/Strata + profit_loss_percent: Zysk/Strata % + refund_amount: Kwota zwrotu + revenue: Dochód + sale_price: Cena wyprzedażowa + shipping_charges: Koszty wysyłki + tax: Podatek + promotion_discount: Promocyjna zniżka + unique_purchases: + title: Unikalne zakupy + description: Wyświetla informację o tym jak wiele unikalnych użytkowników kupiło poszczególne produkty. + product_name: Produkt + sku: Wariant + sold_count: Liczba sprzedanych + users: Użytkownicy + best_selling_products: + title: Najlepiej sprzedające się produkty + description: Najlepiej/najmniej Sprzedające się produkty w maksymalnych zakupionych ilościach. + product_name: Produkt + sku: Wariant + sold_count: Liczba sprzedanych + product_views_to_purchases: + title: Stosunek wyświetleń produktu do sprzedanych + description: Stosunek liczby wyświetleń produktu (strony produktu) do liczby z jaką ten produkt został zakupiony po zobaczeniu. + product_name: Produkt + purchases: Zakupione + views: Wyświetlenia + purchase_to_view_ratio: Stosunek zakupionych do wyświetlonych + returned_products: + title: Zwrócone produkty + description: Liczba produktów zwróconych. + product_name: Produkt + return_count: Liczba zwróconych + sku: Wariant + shipping_cost: + title: Koszty wysyłki + name: Nazwa metody wysyłki + months_name: Miesiąc + shipping_charge: Koszty wysyłki + revenue: Dochód + shipping_cost_percentage: Koszt wysyłki w % + sales_tax: + title: Podatek od sprzedaży + months_name: Miesiac + zone_name: Strefa + sales_tax: Podatek From 3fe878db9c776cbe5d2d35596067f588c694ac31 Mon Sep 17 00:00:00 2001 From: Dorian Sawa Date: Wed, 12 Apr 2017 18:15:15 +0200 Subject: [PATCH 07/12] changes params for Sequel connection(for sqlite db) --- lib/spree_admin_insights/engine.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/spree_admin_insights/engine.rb b/lib/spree_admin_insights/engine.rb index 09797aa..f838f44 100644 --- a/lib/spree_admin_insights/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -22,10 +22,13 @@ def self.activate config.to_prepare &method(:activate).to_proc config.after_initialize do + configuration_hash = (ActiveRecord::Base.configurations[Rails.env] || + Rails.configuration.database_configuration[Rails.env] + ).to_h + configuration_hash.merge!({ 'adapter' => 'sqlite' }) if(configuration_hash['adapter'] == 'sqlite3') + # Connect to applications DB using ruby's Sequel wrapper - db_config = (ActiveRecord::Base.configurations[Rails.env] || - Rails.application.config.database_configuration[Rails.env]) - ::SpreeAdminInsights::ReportDb = Sequel.connect(db_config) + ::SpreeAdminInsights::ReportDb = Sequel.connect(configuration_hash) end end end From e040d1e17763389382c2d42cc9bb64d9a49cf4c2 Mon Sep 17 00:00:00 2001 From: manishrawat0212 Date: Fri, 12 May 2017 18:24:42 +0530 Subject: [PATCH 08/12] promotion analysis fixes --- app/reports/spree/annual_promotional_cost_report.rb | 2 +- app/reports/spree/promotional_cost_report.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/reports/spree/annual_promotional_cost_report.rb b/app/reports/spree/annual_promotional_cost_report.rb index f200e63..618287e 100644 --- a/app/reports/spree/annual_promotional_cost_report.rb +++ b/app/reports/spree/annual_promotional_cost_report.rb @@ -2,7 +2,7 @@ module Spree class AnnualPromotionalCostReport < Spree::PromotionalCostReport DEFAULT_SORTABLE_ATTRIBUTE = :promotion_name HEADERS = { promotion_name: :string, usage_count: :integer, promotion_discount: :integer } - SEARCH_ATTRIBUTES = {} + SEARCH_ATTRIBUTES = { start_date: :promotion_applied_from, end_date: :promotion_applied_till } SORTABLE_ATTRIBUTES = [] def generate diff --git a/app/reports/spree/promotional_cost_report.rb b/app/reports/spree/promotional_cost_report.rb index ce42c02..4f60ee9 100644 --- a/app/reports/spree/promotional_cost_report.rb +++ b/app/reports/spree/promotional_cost_report.rb @@ -2,7 +2,7 @@ module Spree class PromotionalCostReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :promotion_name HEADERS = { promotion_name: :string, usage_count: :integer, promotion_discount: :integer, promotion_code: :string, promotion_start_date: :date, promotion_end_date: :date } - SEARCH_ATTRIBUTES = { start_date: :promotion_created_from, end_date: :promotion_created_till } + SEARCH_ATTRIBUTES = { start_date: :promotion_applied_from, end_date: :promotion_applied_till } SORTABLE_ATTRIBUTES = [:promotion_name, :usage_count, :promotion_discount, :promotion_code, :promotion_start_date, :promotion_end_date] def no_pagination? From 33ac89c8f5b4d2b5b484a82585d8db9b34da13de Mon Sep 17 00:00:00 2001 From: Nimish Date: Wed, 10 May 2017 14:24:53 +0530 Subject: [PATCH 09/12] Port over changes made for solidus --- .../backend/spree_admin_insights/paginator.js | 2 +- .../spree_admin_insights/report_loader.js | 8 +- .../backend/spree_admin_insights/searcher.js | 4 +- .../spree_admin_insights/table_sorter.js | 4 +- .../spree/admin/insights_controller.rb | 75 +++-- app/models/spree/product_decorator.rb | 3 + .../spree/promotion_action_decorator.rb | 3 + .../spree/return_authorization_decorator.rb | 4 + app/models/spree/user_decorator.rb | 3 + .../spree/annual_promotional_cost_report.rb | 58 ---- .../spree/best_selling_products_report.rb | 54 ++-- app/reports/spree/cart_additions_report.rb | 48 ++-- app/reports/spree/cart_removals_report.rb | 48 ++-- app/reports/spree/cart_updations_report.rb | 53 ++-- ...hod_transactions_conversion_rate_report.rb | 135 ++++----- ...payment_method_state_distribution_chart.rb | 39 +++ .../payment_method_transactions_report.rb | 120 +++----- ...yment_method_revenue_distribution_chart.rb | 36 +++ app/reports/spree/product_views_report.rb | 59 ++-- .../product_views_to_cart_additions_report.rb | 78 ++--- .../product_views_to_purchases_report.rb | 73 +++-- app/reports/spree/promotional_cost_report.rb | 181 +++++------- .../promotional_cost_chart.rb | 37 +++ .../usage_count_chart.rb | 41 +++ app/reports/spree/report.rb | 131 +++++++-- app/reports/spree/report/chart.rb | 11 + app/reports/spree/report/date_slicer.rb | 61 ++++ app/reports/spree/report/observation.rb | 49 ++++ app/reports/spree/report/query_fragments.rb | 45 +++ app/reports/spree/report/query_time_scale.rb | 19 ++ app/reports/spree/report/result.rb | 100 +++++++ app/reports/spree/report/timed_observation.rb | 47 +++ app/reports/spree/report/timed_result.rb | 48 ++++ app/reports/spree/returned_products_report.rb | 49 ++-- app/reports/spree/sales_performance_report.rb | 267 ++++++------------ .../profit_loss_chart.rb | 37 +++ .../profit_loss_percent_chart.rb | 36 +++ .../sale_cost_price_chart.rb | 48 ++++ app/reports/spree/sales_tax_report.rb | 121 ++++---- .../monthly_sales_tax_comparison_chart.rb | 39 +++ app/reports/spree/shipping_cost_report.rb | 170 +++++------ .../shipping_cost_distribution_chart.rb | 38 +++ app/reports/spree/trending_search_report.rb | 95 +++---- .../frequency_distribution_pie_chart.rb | 41 +++ app/reports/spree/unique_purchases_report.rb | 52 ++-- app/reports/spree/user_pool_report.rb | 167 ++++------- .../distribution_column_chart.rb | 65 +++++ .../spree/users_not_converted_report.rb | 59 ++-- ..._who_have_not_recently_purchased_report.rb | 35 --- .../users_who_recently_purchased_report.rb | 89 ++++-- .../spree/report_generation_service.rb | 72 ++--- .../spree/admin/insights/download.pdf.erb | 8 +- .../admin/templates/insights/_show.template | 15 +- config/locales/en.yml | 5 + lib/spree_admin_insights/engine.rb | 8 - spree_admin_insights.gemspec | 1 - 56 files changed, 1888 insertions(+), 1306 deletions(-) create mode 100644 app/models/spree/product_decorator.rb create mode 100644 app/models/spree/promotion_action_decorator.rb create mode 100644 app/models/spree/return_authorization_decorator.rb create mode 100644 app/models/spree/user_decorator.rb delete mode 100644 app/reports/spree/annual_promotional_cost_report.rb create mode 100644 app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb create mode 100644 app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb create mode 100644 app/reports/spree/promotional_cost_report/promotional_cost_chart.rb create mode 100644 app/reports/spree/promotional_cost_report/usage_count_chart.rb create mode 100644 app/reports/spree/report/chart.rb create mode 100644 app/reports/spree/report/date_slicer.rb create mode 100644 app/reports/spree/report/observation.rb create mode 100644 app/reports/spree/report/query_fragments.rb create mode 100644 app/reports/spree/report/query_time_scale.rb create mode 100644 app/reports/spree/report/result.rb create mode 100644 app/reports/spree/report/timed_observation.rb create mode 100644 app/reports/spree/report/timed_result.rb create mode 100644 app/reports/spree/sales_performance_report/profit_loss_chart.rb create mode 100644 app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb create mode 100644 app/reports/spree/sales_performance_report/sale_cost_price_chart.rb create mode 100644 app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb create mode 100644 app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb create mode 100644 app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb create mode 100644 app/reports/spree/user_pool_report/distribution_column_chart.rb delete mode 100644 app/reports/spree/users_who_have_not_recently_purchased_report.rb diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js b/app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js index 6ad79ae..e3bb41b 100644 --- a/app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/paginator.js @@ -91,7 +91,7 @@ Paginator.prototype.removePagination = function(currentElement) { sorted_attributes = this.tableSorter.fetchSortedAttribute(), attribute = sorted_attributes[0], sortOrder = sorted_attributes[1], - requestUrl = $element.data('url') + '&sort%5Battribute%5D=' + attribute + '&sort%5Btype%5D=' + sortOrder + '&' + $('#filter-search').serialize() + '&no_pagination=true'; + requestUrl = $element.data('url') + '&sort%5Battribute%5D=' + attribute + '&sort%5Btype%5D=' + sortOrder + '&' + $('#filter-search').serialize() + '&paginate=false'; $(currentElement).attr('href', requestUrl); _this.reportLoader.requestUrl = requestUrl; $element.val(''); diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js index 520e2a6..de473b9 100644 --- a/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js @@ -83,9 +83,9 @@ ReportLoader.prototype.bindEvents = function() { ReportLoader.prototype.resetFilters = function(event) { event.preventDefault(); var $element = $(event.target), - noPagination = this.removePaginationButton.closest('span').hasClass('hide'); - $element.attr('href', this.perPageSelector.data('url') + '&no_pagination=' + noPagination); - $element.data('url', this.perPageSelector.data('url') + '&no_pagination=' + noPagination); + paginated = !this.removePaginationButton.closest('span').hasClass('hide'); + $element.attr('href', this.perPageSelector.data('url') + '&paginate=' + paginated); + $element.data('url', this.perPageSelector.data('url') + '&paginate=' + paginated); this.loadChart($element); this.searcherObject.clearSearchFields(); }; @@ -187,7 +187,7 @@ ReportLoader.prototype.populateInsightsData = function(data) { ReportLoader.prototype.setDownloadLinksPath = function($selectedOption) { var _this = this; $.each(this.downloadLinks, function() { - $(this).attr('href', $(this).data('url') + '?id=' + _this.$selectList.val() + '&no_pagination=true'); + $(this).attr('href', $(this).data('url') + '?id=' + _this.$selectList.val() + '&paginate=false'); }); }; diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js b/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js index 282bf6a..f4c5ae8 100644 --- a/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/searcher.js @@ -28,12 +28,12 @@ Searcher.prototype.refreshSearcher = function($selectedInsight, data) { _this.setFormActions(_this.$filterForm, requestPath); _this.$filterForm.on('submit', function() { - var noPagination = _this.reportLoader.removePaginationButton.closest('span').hasClass('hide'); + var paginated = !_this.reportLoader.removePaginationButton.closest('span').hasClass('hide'); _this.addSearchStatus(); $.ajax({ type: "GET", url: _this.$filterForm.attr('action'), - data: _this.$filterForm.serialize() + "&per_page=" + _this.reportLoader.pageSelector.find(':selected').attr('value') + '&no_pagination=' + noPagination, + data: _this.$filterForm.serialize() + "&per_page=" + _this.reportLoader.pageSelector.find(':selected').attr('value') + '&paginate=' + paginated, dataType: 'json', success: function(data) { _this.clearFormFields(); diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js b/app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js index bd4d64f..503ab01 100644 --- a/app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/table_sorter.js @@ -11,8 +11,8 @@ TableSorter.prototype.bindEvents = function() { this.$insightsTableList.on('click', '#admin-insight .sortable-link', function() { event.preventDefault(); var currentPage = _this.paginatorDiv.find('li.active a').html() - 1, - noPagination = _this.reportLoader.removePaginationButton.closest('span').hasClass('hide'), - requestPath = $(event.target).attr('href') + '&' + $('#filter-search').serialize() + '&page=' + currentPage + "&per_page=" + _this.reportLoader.pageSelector.find(':selected').attr('value') + '&no_pagination=' + noPagination; + paginated = !_this.reportLoader.removePaginationButton.closest('span').hasClass('hide'), + requestPath = $(event.target).attr('href') + '&' + $('#filter-search').serialize() + '&page=' + currentPage + "&per_page=" + _this.reportLoader.pageSelector.find(':selected').attr('value') + '&paginate=' + paginated; _this.reportLoader.requestUrl = requestPath; $.ajax({ diff --git a/app/controllers/spree/admin/insights_controller.rb b/app/controllers/spree/admin/insights_controller.rb index e54c73d..fb737b6 100644 --- a/app/controllers/spree/admin/insights_controller.rb +++ b/app/controllers/spree/admin/insights_controller.rb @@ -2,6 +2,7 @@ module Spree module Admin class InsightsController < Spree::Admin::BaseController before_action :ensure_report_exists, :set_default_pagination, only: [:show, :download] + before_action :set_reporting_period, only: [:index, :show, :download] before_action :load_reports, only: [:index, :show] def index @@ -12,46 +13,29 @@ def index end def show - @headers, @stats, @total_pages, @search_attributes, @chart_json, @resource = ReportGenerationService.generate_report( - @report_name, - params.merge(@pagination_hash) - ) - - @report_data_json = { - current_page: params[:page] || 0, - headers: @headers, - report_type: params[:type], - request_path: request.path, - search_attributes: @search_attributes, - stats: @stats, - total_pages: @total_pages, - url: request.url, - searched_fields: params[:search], - per_page: @pagination_hash[:records_per_page], - chart_json: @chart_json, - pagination_required: !@resource.no_pagination? - } + report = ReportGenerationService.generate_report(@report_name, params.merge(@pagination_hash)) + @report_data = shared_data.merge(report.to_h) respond_to do |format| format.html { render :index } - format.json { render json: @report_data_json } + format.json { render json: @report_data } end end def download - @headers, @stats = ReportGenerationService.generate_report(@report_name, params.merge(@pagination_hash)) + @report = ReportGenerationService.generate_report(@report_name, params.merge(@pagination_hash)) respond_to do |format| format.csv do - send_data ReportGenerationService.download(@headers, @stats), + send_data ReportGenerationService.download(@report), filename: "#{ @report_name.to_s }.csv" end format.xls do - send_data ReportGenerationService.download({ col_sep: "\t" }, @headers, @stats), + send_data ReportGenerationService.download(@report, { col_sep: "\t" }), filename: "#{ @report_name.to_s }.xls" end format.text do - send_data ReportGenerationService.download(@headers, @stats), + send_data ReportGenerationService.download(@report), filename: "#{ @report_name.to_s }.txt" end format.pdf do @@ -65,27 +49,60 @@ def download private def ensure_report_exists @report_name = params[:id].to_sym - unless ReportGenerationService::REPORTS[get_reports_type].include? @report_name + unless ReportGenerationService.report_exists?(get_reports_type, @report_name) redirect_to admin_insights_path, alert: Spree.t(:not_found, scope: [:reports]) end end def load_reports - @reports = ReportGenerationService::REPORTS[get_reports_type] + @reports = ReportGenerationService.reports_for_type(get_reports_type) + end + + def shared_data + { + current_page: params[:page] || 0, + report_type: params[:type], + request_path: request.path, + url: request.url, + searched_fields: params[:search], + } end def get_reports_type params[:type] = if params[:type] params[:type].to_sym else - session[:report_category].try(:to_sym) || ReportGenerationService::REPORTS.keys.first + session[:report_category].try(:to_sym) || ReportGenerationService.default_report_type end session[:report_category] = params[:type] end + def set_reporting_period + if params[:search].present? + if params[:search][:start_date] == "" + # When clicking on 'x' to remove the filter + params[:search][:start_date] = nil + else + params[:search][:start_date] = params[:search][:start_date] || session[:search_start_date] + end + if params[:search][:end_date] == "" + params[:search][:end_date] = nil + else + params[:search][:end_date] = params[:search][:end_date].presence || session[:search_end_date] + end + else + params[:search] = {} + params[:search][:start_date] = session[:search_start_date] + params[:search][:end_date] = session[:search_end_date] + end + session[:search_start_date] = params[:search][:start_date] + session[:search_end_date] = params[:search][:end_date] + end + def set_default_pagination - @pagination_hash = {} - if params[:no_pagination] != 'true' + @pagination_hash = { paginate: false } + unless params[:paginate] == 'false' + @pagination_hash[:paginate] = true @pagination_hash[:records_per_page] = params[:per_page].try(:to_i) || Spree::Config[:records_per_page] @pagination_hash[:offset] = params[:page].to_i * @pagination_hash[:records_per_page] end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb new file mode 100644 index 0000000..47afb2c --- /dev/null +++ b/app/models/spree/product_decorator.rb @@ -0,0 +1,3 @@ +Spree::Product.class_eval do + has_many :page_view_events, -> { viewed.product }, class_name: 'Spree::PageEvent', foreign_key: :target_id +end diff --git a/app/models/spree/promotion_action_decorator.rb b/app/models/spree/promotion_action_decorator.rb new file mode 100644 index 0000000..e957bc9 --- /dev/null +++ b/app/models/spree/promotion_action_decorator.rb @@ -0,0 +1,3 @@ +Spree::PromotionAction.class_eval do + has_one :adjustment, -> { promotion }, class_name: 'Spree::Adjustment', foreign_key: :source_id +end diff --git a/app/models/spree/return_authorization_decorator.rb b/app/models/spree/return_authorization_decorator.rb new file mode 100644 index 0000000..651305b --- /dev/null +++ b/app/models/spree/return_authorization_decorator.rb @@ -0,0 +1,4 @@ +Spree::ReturnAuthorization.class_eval do + has_many :variants, through: :inventory_units + has_many :products, through: :variants +end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb new file mode 100644 index 0000000..8654a38 --- /dev/null +++ b/app/models/spree/user_decorator.rb @@ -0,0 +1,3 @@ +Spree::User.class_eval do + has_many :spree_orders, class_name: 'Spree::Order' +end diff --git a/app/reports/spree/annual_promotional_cost_report.rb b/app/reports/spree/annual_promotional_cost_report.rb deleted file mode 100644 index 618287e..0000000 --- a/app/reports/spree/annual_promotional_cost_report.rb +++ /dev/null @@ -1,58 +0,0 @@ -module Spree - class AnnualPromotionalCostReport < Spree::PromotionalCostReport - DEFAULT_SORTABLE_ATTRIBUTE = :promotion_name - HEADERS = { promotion_name: :string, usage_count: :integer, promotion_discount: :integer } - SEARCH_ATTRIBUTES = { start_date: :promotion_applied_from, end_date: :promotion_applied_till } - SORTABLE_ATTRIBUTES = [] - - def generate - super - data = [] - group_by_promotion_name.each_pair do |promotion_name, collection| - data << { promotion_name: promotion_name, promotion_discount: collection.sum { |r| r[:promotion_discount] }, usage_count: collection.sum { |r| r[:usage_count] } } - end - data - end - - def chart_data - data = generate - total_discount = (data.sum { |r| r[:promotion_discount] } / 100) - data.map { |r| { name: r[:promotion_name], y: (r[:promotion_discount] / total_discount).to_f } } - end - - def chart_json - { - chart: true, - charts: [ - { - name: 'annual-promotional-cost', - json: { - chart: { type: 'pie' }, - title: { - useHTML: true, - text: 'Annual Promotional Cost' - }, - tooltip: { - pointFormat: 'Cost %: {point.percentage:.1f}%' - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - series: [{ - name: 'Annual Promotion', - data: chart_data - }] - } - } - ] - } - end - end -end diff --git a/app/reports/spree/best_selling_products_report.rb b/app/reports/spree/best_selling_products_report.rb index d93e8c3..05b6e98 100644 --- a/app/reports/spree/best_selling_products_report.rb +++ b/app/reports/spree/best_selling_products_report.rb @@ -1,34 +1,42 @@ module Spree class BestSellingProductsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :sold_count - HEADERS = { sku: :string, product_name: :string, sold_count: :integer } - SEARCH_ATTRIBUTES = { start_date: :orders_completed_from, end_date: :orders_completed_to } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :sold_count] + HEADERS = { sku: :string, product_name: :string, sold_count: :integer } + SEARCH_ATTRIBUTES = { start_date: :orders_completed_from, end_date: :orders_completed_to } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :sold_count] - def initialize(options) - super - @name = @search[:name].present? ? "%#{ @search[:name] }%" : '%' - @sortable_type = :desc if options[:sort].blank? - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :sku, :sold_count] + + def sku + @sku.presence || @product_name + end + end end - def generate - ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. - join(:spree_orders___orders, id: :order_id). - join(:spree_variants___variants, variants__id: :line_items__variant_id). - join(:spree_products___products, products__id: :variants__product_id). - where(orders__state: 'complete'). - where(orders__completed_at: @start_date..@end_date). #filter by params - group(:variant_id). - order(sortable_sequel_expression) + def report_query + Spree::LineItem + .joins(:order) + .joins(:variant) + .joins(:product) + .where(Spree::Product.arel_table[:name].matches(search_name)) + .where(spree_orders: { state: 'complete' }) + .where(spree_orders: { completed_at: reporting_period }) + .group(:variant_id, :product_name, :product_slug, 'spree_variants.sku') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_variants.sku as sku', + 'sum(quantity) as sold_count' + ) end - def select_columns(dataset) - dataset.select{[ - products__name.as(product_name), - Sequel.as(IF(STRCMP(variants__sku, ''), variants__sku, products__name), :sku), - sum(quantity).as(sold_count) - ]} + private def search_name + search[:name].present? ? "%#{ search[:name] }%" : '%' end + end end diff --git a/app/reports/spree/cart_additions_report.rb b/app/reports/spree/cart_additions_report.rb index 6551278..9fa059c 100644 --- a/app/reports/spree/cart_additions_report.rb +++ b/app/reports/spree/cart_additions_report.rb @@ -1,32 +1,36 @@ module Spree class CartAdditionsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { sku: :string, product_name: :string, additions: :integer, quantity_change: :integer } - SEARCH_ATTRIBUTES = { start_date: :product_added_from, end_date: :product_added_to } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :additions, :quantity_change] + HEADERS = { sku: :string, product_name: :string, additions: :integer, quantity_change: :integer } + SEARCH_ATTRIBUTES = { start_date: :product_added_from, end_date: :product_added_to } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :additions, :quantity_change] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :additions, :quantity_change, :sku] - def generate - SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. - join(:spree_variants___variants, id: :variant_id). - join(:spree_products___products, id: :product_id). - where(cart_events__activity: 'add'). - where(cart_events__created_at: @start_date..@end_date). - group(:variant_id). - order(sortable_sequel_expression) + def sku + @sku.presence || @product_name + end + end end - def select_columns(dataset) - dataset.select{[ - products__name.as(product_name), - Sequel.as(IF(STRCMP(variants__sku, ''), variants__sku, products__name), :sku), - Sequel.as(count(:products__name), :additions), - Sequel.as(sum(cart_events__quantity), :quantity_change) - ]} + def report_query + Spree::CartEvent + .added + .joins(:variant) + .joins(:product) + .where(created_at: reporting_period) + .group('product_name', 'product_slug', 'spree_variants.sku') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_variants.sku as sku', + 'count(spree_products.name) as additions', + 'sum(spree_cart_events.quantity) as quantity_change' + ) end end end diff --git a/app/reports/spree/cart_removals_report.rb b/app/reports/spree/cart_removals_report.rb index 10d008b..eeece76 100644 --- a/app/reports/spree/cart_removals_report.rb +++ b/app/reports/spree/cart_removals_report.rb @@ -1,32 +1,36 @@ module Spree class CartRemovalsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { sku: :string, product_name: :string, removals: :integer, quantity_change: :integer } - SEARCH_ATTRIBUTES = { start_date: :product_removed_from, end_date: :product_removed_to } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :removals, :quantity_change] + HEADERS = { sku: :string, product_name: :string, removals: :integer, quantity_change: :integer } + SEARCH_ATTRIBUTES = { start_date: :product_removed_from, end_date: :product_removed_to } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :removals, :quantity_change] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :removals, :quantity_change, :sku] - def generate - SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. - join(:spree_variants___variants, id: :variant_id). - join(:spree_products___products, id: :product_id). - where(cart_events__activity: 'remove'). - where(cart_events__created_at: @start_date..@end_date). #filter by params - group(:variant_id). - order(sortable_sequel_expression) + def sku + @sku.presence || @product_name + end + end end - def select_columns(dataset) - dataset.select{[ - products__name.as(product_name), - Sequel.as(IF(STRCMP(variants__sku, ''), variants__sku, products__name), :sku), - Sequel.as(count(:products__name), :removals), - Sequel.as(sum(cart_events__quantity), :quantity_change) - ]} + def report_query + Spree::CartEvent + .removed + .joins(:variant) + .joins(:product) + .where(created_at: reporting_period) + .group('product_name', 'product_slug', 'spree_variants.sku') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_variants.sku as sku', + 'count(spree_products.name) as removals', + 'sum(spree_cart_events.quantity) as quantity_change' + ) end end end diff --git a/app/reports/spree/cart_updations_report.rb b/app/reports/spree/cart_updations_report.rb index be3aad7..b377cca 100644 --- a/app/reports/spree/cart_updations_report.rb +++ b/app/reports/spree/cart_updations_report.rb @@ -1,33 +1,40 @@ module Spree class CartUpdationsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { sku: :string, product_name: :string, updations: :integer, quantity_increase: :integer, quantity_decrease: :integer } - SEARCH_ATTRIBUTES = { start_date: :product_updated_from, end_date: :product_updated_to } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :updations, :quantity_increase, :quantity_decrease] + HEADERS = { sku: :string, product_name: :string, updations: :integer, quantity_increase: :integer, quantity_decrease: :integer } + SEARCH_ATTRIBUTES = { start_date: :product_updated_from, end_date: :product_updated_to } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :updations, :quantity_increase, :quantity_decrease] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :updations, :quantity_increase, :sku, :quantity_decrease] - def generate - SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. - join(:spree_variants___variants, id: :variant_id). - join(:spree_products___products, id: :product_id). - where(activity: 'update'). - where(cart_events__created_at: @start_date..@end_date). #filter by params - group(:variant_id). - order(sortable_sequel_expression) + def sku + @sku.presence || @product_name + end + end end - def select_columns(dataset) - dataset.select{[ - products__name.as(product_name), - Sequel.as(IF(STRCMP(variants__sku, ''), variants__sku, products__name), :sku), - Sequel.as(count(:products__name), :updations), - Sequel.as(sum(IF(cart_events__quantity >= 0, cart_events__quantity, 0)), :quantity_increase), - Sequel.as(sum(IF(cart_events__quantity <= 0, cart_events__quantity, 0)), :quantity_decrease) - ]} + def report_query + quantity_increase_sql = "CASE WHEN quantity > 0 then spree_cart_events.quantity ELSE 0 END" + quantity_decrease_sql = "CASE WHEN quantity < 0 then spree_cart_events.quantity ELSE 0 END" + + Spree::CartEvent + .updated + .joins(:variant) + .joins(:product) + .where(created_at: reporting_period) + .group('product_name', 'product_slug', 'spree_variants.sku') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_variants.sku as sku', + 'count(spree_products.name) as updations', + "SUM(#{ quantity_increase_sql }) as quantity_increase", + "SUM(#{ quantity_decrease_sql }) as quantity_decrease" + ) end end end diff --git a/app/reports/spree/payment_method_transactions_conversion_rate_report.rb b/app/reports/spree/payment_method_transactions_conversion_rate_report.rb index cb5040f..cc9bfda 100644 --- a/app/reports/spree/payment_method_transactions_conversion_rate_report.rb +++ b/app/reports/spree/payment_method_transactions_conversion_rate_report.rb @@ -1,93 +1,74 @@ module Spree class PaymentMethodTransactionsConversionRateReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :payment_method_name - HEADERS = { payment_method_name: :string, payment_state: :string, months_name: :string, count: :integer } - SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_to } - SORTABLE_ATTRIBUTES = [:payment_method_name, :successful_payments_count, :failed_payments_count, :pending_payments_count, :invalid_payments_count] + HEADERS = { payment_method_name: :string, payment_state: :string, months_name: :string, count: :integer } + SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_to } + SORTABLE_ATTRIBUTES = [:payment_method_name, :successful_payments_count, :failed_payments_count, :pending_payments_count, :invalid_payments_count] - def no_pagination? - true - end - - def generate - payment_methods = SpreeAdminInsights::ReportDb[:spree_payment_methods___payment_methods]. - join(:spree_payments___payments, payment_method_id: :id). - where(payments__created_at: @start_date..@end_date). #filter by params - select{[ - payment_method_id, - Sequel.as(name, :payment_method_name), - Sequel.as(IF(STRCMP(state, 'pending'), state, concat('capturing ', state)), :payment_state), - Sequel.as(MONTHNAME(:payments__created_at), :month_name), - Sequel.as(MONTH(:payments__created_at), :number), - Sequel.as(YEAR(:payments__created_at), :year) - ]} + class Result < Spree::Report::TimedResult + charts PaymentMethodStateDistributionChart - group_by_months = SpreeAdminInsights::ReportDb[payment_methods]. - group(:months_name, :payment_method_name, :payment_state). - order(:year, :number). - select{[ - payment_method_name, - number, - payment_state, - year, - Sequel.as(concat(month_name, ' ', year), :months_name), - Sequel.as(COUNT(payment_method_id), :count), - ]} + def build_empty_observations + super + @_payment_methods = @results.collect { |result| result['payment_method_name'] }.uniq + @observations = @_payment_methods.collect do |payment_method_name| + payment_states = @results + .select { |result| result['payment_method_name'] == payment_method_name } + .collect { |result| result['payment_state'] } + .uniq - grouped_by_payment_method_name = group_by_months.all.group_by { |record| record[:payment_method_name] } - data = [] - grouped_by_payment_method_name.each_pair do |name, collection| - collection.group_by { |r| r[:payment_state] }.each_pair do |state, collection| - data << fill_missing_values({ payment_method_name: name, payment_state: state, count: 0 }, collection) - end + payment_states.collect do |state| + @observations.collect do |observation| + _d_observation = observation.dup + _d_observation.payment_method_name = payment_method_name + _d_observation.payment_state = state + _d_observation.count = 0 + _d_observation + end + end + end.flatten end - @data = data.flatten - end - def group_by_payment_method_name - @grouped_by_payment_method_name ||= @data.group_by { |record| record[:payment_method_name] } - end + class Observation < Spree::Report::TimedObservation + observation_fields [:payment_method_name, :payment_state, :count] - def chart_data - { - months_name: group_by_payment_method_name.first.try(:second).try(:map) { |record| record[:months_name] }, - collection: group_by_payment_method_name - } - end - - def chart_json - { - chart: true, - charts: chart_data[:collection].map do |method_name, collection| - { - id: 'payment-state-' + method_name, - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: "#{ method_name } Conversion Status" - }, + def payment_state + if @payment_state == 'pending' + @payment_state + else + "capturing #{ @payment_state }" + end + end - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Count' } - }, - tooltip: { valuePrefix: '#' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: collection.group_by { |r| r[:payment_state] }.map { |key, value| { name: key, data: value.map { |r| r[:count].to_i } } } - } - } + def describes?(result, time_scale) + (result['payment_method_name'] == payment_method_name && result['payment_state'] == @payment_state) && super end - } + end + end + + def report_query + Spree::Report::QueryFragments + .from_subquery(payment_methods) + .group(*time_scale_columns_to_s, 'payment_method_name', 'payment_state') + .order(*time_scale_columns) + .project( + *time_scale_columns, + 'payment_method_name', + 'payment_state', + 'COUNT(payment_method_id) as count' + ) end - def select_columns(dataset) - dataset + private def payment_methods + Spree::PaymentMethod + .joins(:payments) + .where(spree_payments: { created_at: reporting_period }) + .select( + 'spree_payment_methods.id as payment_method_id', + 'name as payment_method_name', + 'state as payment_state', + *time_scale_selects('spree_payments') + ) end end end diff --git a/app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb b/app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb new file mode 100644 index 0000000..67da3fd --- /dev/null +++ b/app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb @@ -0,0 +1,39 @@ +class Spree::PaymentMethodTransactionsConversionRateReport::PaymentMethodStateDistributionChart + attr_accessor :chart_data + + def initialize(result) + @time_dimension = result.time_dimension + @grouped_by_payment_method = result.observations.group_by(&:payment_method_name) + @time_series = [] + @time_series = @grouped_by_payment_method.values.first.collect { |observation| observation.send(@time_dimension) } if @grouped_by_payment_method.first.present? + end + + def to_h + @grouped_by_payment_method.collect do |method_name, observations| + { + id: 'payment-state-' + method_name, + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: %Q(#{ method_name } Conversion Status + ) + }, + + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Count' } + }, + tooltip: { valuePrefix: '#' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: observations.group_by(&:payment_state).map { |key, value| { name: key, data: value.map(&:count) } } + } + } + end + end +end diff --git a/app/reports/spree/payment_method_transactions_report.rb b/app/reports/spree/payment_method_transactions_report.rb index fcd1234..0c6e079 100644 --- a/app/reports/spree/payment_method_transactions_report.rb +++ b/app/reports/spree/payment_method_transactions_report.rb @@ -1,88 +1,60 @@ module Spree class PaymentMethodTransactionsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :payment_method_name - HEADERS = { payment_method_name: :string, months_name: :string, payment_amount: :integer } - SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_till } - SORTABLE_ATTRIBUTES = [] - - def no_pagination? - true - end + HEADERS = { payment_method_name: :string, payment_amount: :integer } + SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_till } + SORTABLE_ATTRIBUTES = [] + + class Result < Spree::Report::TimedResult + charts PaymentMethodRevenueDistributionChart + + def build_empty_observations + super + @_payment_methods = @results.collect { |result| result['payment_method_name'] }.uniq + @observations = @_payment_methods.collect do |payment_method_name| + @observations.collect do |observation| + _d_observation = observation.dup + _d_observation.payment_amount = 0 + _d_observation.payment_method_name = payment_method_name + _d_observation + end + end.flatten + end - def generate - payments = SpreeAdminInsights::ReportDb[:spree_payment_methods___payment_methods]. - join(:spree_payments___payments, payment_method_id: :id). - where(payments__created_at: @start_date..@end_date). #filter by params - select{[ - Sequel.as(payment_methods__name, :payment_method_name), - Sequel.as(payments__amount, :payment_amount), - Sequel.as(MONTHNAME(:payments__created_at), :month_name), - Sequel.as(MONTH(:payments__created_at), :number), - Sequel.as(YEAR(:payments__created_at), :year) - ]} + class Observation < Spree::Report::TimedObservation + observation_fields [:payment_method_name, :payment_amount] - group_by_months = SpreeAdminInsights::ReportDb[payments]. - group(:months_name, :payment_method_name). - order(:year, :number). - select{[ - number, - payment_method_name, - year, - Sequel.as(concat(month_name, ' ', year), :months_name), - Sequel.as(SUM(payment_amount), :payment_amount) - ]} + def describes?(result, time_scale) + (result['payment_method_name'] == payment_method_name) && super + end - grouped_by_payment_method_name = group_by_months.all.group_by { |record| record[:payment_method_name] } - data = [] - grouped_by_payment_method_name.each_pair do |name, collection| - data << fill_missing_values({ payment_method_name: name, payment_amount: 0 }, collection) + def payment_amount + @payment_amount.to_f + end end - @data = data.flatten - end - - def group_by_payment_method_name - @grouped_by_payment_method_name ||= @data.group_by { |record| record[:payment_method_name] } - end - - def chart_data - { - months_name: group_by_payment_method_name.first.try(:second).try(:map) { |record| record[:months_name] }, - collection: group_by_payment_method_name - } end - def chart_json - { - chart: true, - charts: [ - { - id: 'payment-methods', - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: "Payment Methods" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'value($)' } - }, - tooltip: { valuePrefix: '$' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: chart_data[:collection].map { |key, value| { name: key, data: value.map { |r| r[:payment_amount].to_f } } } - } - } - ] - } + def report_query + Spree::Report::QueryFragments + .from_subquery(payments) + .group(*time_scale_columns_to_s, 'payment_method_name') + .order(*time_scale_columns) + .project( + *time_scale_columns, + 'payment_method_name', + 'SUM(payment_amount) as payment_amount' + ) end - def select_columns(dataset) - dataset + private def payments + Spree::PaymentMethod + .joins(:payments) + .where(spree_payments: { created_at: reporting_period }) + .select( + *time_scale_selects('spree_payments'), + 'spree_payment_methods.name as payment_method_name', + 'spree_payments.amount as payment_amount', + ) end end end diff --git a/app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb b/app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb new file mode 100644 index 0000000..c7310b7 --- /dev/null +++ b/app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb @@ -0,0 +1,36 @@ +class Spree::PaymentMethodTransactionsReport::PaymentMethodRevenueDistributionChart + def initialize(result) + @time_dimension = result.time_dimension + @grouped_by_payment_method = result.observations.group_by(&:payment_method_name) + @time_series = [] + if @grouped_by_payment_method.values.first.present? + @time_series = @grouped_by_payment_method.values.first.collect { |observation| observation.send(@time_dimension) } + end + end + + def to_h + { + id: 'payment-methods', + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: "Payment Methods" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'value($)' } + }, + tooltip: { valuePrefix: '$' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: @grouped_by_payment_method.collect { |key, value| { name: key, data: value.map(&:payment_amount) } + } + } + } + end +end diff --git a/app/reports/spree/product_views_report.rb b/app/reports/spree/product_views_report.rb index 5b72a61..dee1348 100644 --- a/app/reports/spree/product_views_report.rb +++ b/app/reports/spree/product_views_report.rb @@ -5,37 +5,42 @@ class ProductViewsReport < Spree::Report SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till, name: :name} SORTABLE_ATTRIBUTES = [:product_name, :views, :users, :guest_sessions] - def initialize(options) - super - @name = @search[:name].present? ? "%#{ @search[:name] }%" : '%' - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } - def generate - unique_session_results = ::SpreeAdminInsights::ReportDb[:spree_products___products]. - join(:spree_page_events___page_events, target_id: :id). - where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). - where(page_events__created_at: @start_date..@end_date).where(Sequel.ilike(:products__name, @name)). - group(:product_name, :page_events__actor_id, :page_events__session_id). - select{[ - products__name.as(product_name), - count('*').as(total_views_per_session), - page_events__session_id.as(session_id), - page_events__actor_id.as(actor_id) - ]}.as(:unique_session_results) + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :views, :users, :guest_sessions] + end + end - ::SpreeAdminInsights::ReportDb[unique_session_results]. - group(:product_name). - order(sortable_sequel_expression) + def report_query + viewed_events = + Spree::Product + .where(Spree::Product.arel_table[:name].matches(search_name)) + .joins(:page_view_events) + .where(spree_page_events: { created_at: reporting_period }) + .group('product_name', 'product_slug', 'spree_page_events.actor_id', 'spree_page_events.session_id') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'COUNT(*) as total_views_per_session', + 'spree_page_events.session_id as session_id', + 'spree_page_events.actor_id as actor_id' + ) + Spree::Report::QueryFragments + .from_subquery(viewed_events) + .group('product_name', 'product_slug') + .project( + 'product_name', + 'product_slug', + 'SUM(total_views_per_session) as views', + 'COUNT(DISTINCT actor_id) as users', + '(COUNT(DISTINCT session_id) - COUNT(actor_id)) as guest_sessions' + ) end - def select_columns(dataset) - dataset.select{[ - product_name, - sum(total_views_per_session).as(views), - count(DISTINCT actor_id).as(users), - (COUNT(DISTINCT session_id) - COUNT(actor_id)).as(guest_sessions) - ]} + private def search_name + search[:name].present? ? "%#{ search[:name] }%" : '%' end end end diff --git a/app/reports/spree/product_views_to_cart_additions_report.rb b/app/reports/spree/product_views_to_cart_additions_report.rb index 72c108a..6640920 100644 --- a/app/reports/spree/product_views_to_cart_additions_report.rb +++ b/app/reports/spree/product_views_to_cart_additions_report.rb @@ -1,49 +1,53 @@ module Spree class ProductViewsToCartAdditionsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { product_name: :string, views: :integer, cart_additions: :integer, cart_to_view_ratio: :string } - SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till } - SORTABLE_ATTRIBUTES = [:product_name, :views, :cart_additions] + HEADERS = { product_name: :string, views: :integer, cart_additions: :integer, cart_to_view_ratio: :string } + SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till } + SORTABLE_ATTRIBUTES = [:product_name, :views, :cart_additions] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } - def generate(options = {}) - cart_additions = SpreeAdminInsights::ReportDb[:spree_cart_events___cart_events]. - join(:spree_variants___variants, id: :variant_id). - join(:spree_products___products, id: :product_id). - where(cart_events__activity: 'add'). - where(cart_events__created_at: @start_date..@end_date). #filter by params - group(:product_name). - select{[( - :products__name___product_name), - Sequel.as(sum(cart_events__quantity), :cart_additions) - ]}.as(:cart_additions) + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :views, :cart_additions, :cart_to_view_ratio] + def cart_to_view_ratio + (cart_additions.to_f / views.to_f).round(2) + end + end + end - total_views_results = ::SpreeAdminInsights::ReportDb[:spree_products___products]. - join(:spree_page_events___page_events, target_id: :id). - where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). - group(:product_name). - select{[ - products__name.as(product_name), - count('*').as(views) - ]} + def report_query + cart_additions = + Spree::CartEvent + .added + .joins(:variant) + .joins(:product) + .where(created_at: reporting_period) + .group('spree_products.name', 'spree_products.slug') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'SUM(spree_cart_events.quantity) as cart_additions' + ) + total_views = + Spree::Product + .joins(:page_view_events) + .group(:name) + .select( + 'spree_products.name as product_name', + 'COUNT(*) as views' + ) - ::SpreeAdminInsights::ReportDb[total_views_results]. - join(cart_additions, product_name: :product_name). - order(sortable_sequel_expression) + Spree::Report::QueryFragments + .from_join(cart_additions, total_views, "q1.product_name = q2.product_name") + .project( + 'q1.product_name', + 'q1.product_slug', + 'q2.views', + 'q1.cart_additions' + ) end - def select_columns(dataset) - dataset.select{[ - cart_additions__product_name, - views, - cart_additions__cart_additions, - Sequel.as(ROUND(cart_additions__cart_additions/ views, 2), :cart_to_view_ratio) - ]} - end end end diff --git a/app/reports/spree/product_views_to_purchases_report.rb b/app/reports/spree/product_views_to_purchases_report.rb index 8dea56e..c3a65f9 100644 --- a/app/reports/spree/product_views_to_purchases_report.rb +++ b/app/reports/spree/product_views_to_purchases_report.rb @@ -1,43 +1,54 @@ module Spree class ProductViewsToPurchasesReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { product_name: :string, views: :integer, purchases: :integer, purchase_to_view_ratio: :integer } - SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till } - SORTABLE_ATTRIBUTES = [:product_name, :views, :purchases] + HEADERS = { product_name: :string, views: :integer, purchases: :integer, purchase_to_view_ratio: :integer } + SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till } + SORTABLE_ATTRIBUTES = [:product_name, :views, :purchases] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :views, :purchases, :purchase_to_view_ratio] + + def purchase_to_view_ratio # This is inconsistent across postgres and mysql + (purchases.to_f / views.to_f).round(2) + end + end end - def generate(options = {}) - line_items = ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. - join(:spree_orders___orders, id: :order_id). - join(:spree_variants___variants, variants__id: :line_items__variant_id). - join(:spree_products___products, products__id: :variants__product_id). - where(orders__state: 'complete'). - where(orders__created_at: @start_date..@end_date). #filter by params - select{[line_items__quantity, - line_items__id, - line_items__variant_id, - sum(quantity).as(purchases), - products__name.as(product_name), - products__id.as(product_id)]}. - group(:products__name).as(:line_items) + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + def report_query + page_events_ar = Arel::Table.new(:spree_page_events) + purchase_line_items_ar = Arel::Table.new(:purchase_line_items) - ::SpreeAdminInsights::ReportDb[line_items].join(:spree_page_events___page_events, page_events__target_id: :product_id). - where(page_events__target_type: 'Spree::Product', page_events__activity: 'view'). - group(:product_name). - order(sortable_sequel_expression) + Spree::Report::QueryFragments.from_subquery(purchase_line_items, as: :purchase_line_items) + .join(page_events_ar) + .on(page_events_ar[:target_id].eq(purchase_line_items_ar[:product_id])) + .where(page_events_ar[:target_type].eq(Arel::Nodes::Quoted.new('Spree::Product'))) + .where(page_events_ar[:activity].eq(Arel::Nodes::Quoted.new('view'))) + .group(purchase_line_items_ar[:product_id], purchase_line_items_ar[:product_name], + purchase_line_items_ar[:product_slug], purchase_line_items_ar[:purchases]) + .project( + 'product_name', + 'product_slug', + 'COUNT(*) as views', + 'purchases' + ) end - def select_columns(dataset) - dataset.select{[ - product_name, - count('*').as(views), - purchases, - Sequel.as(ROUND(purchases / count('*'), 2), :purchase_to_view_ratio) - ]} + private def purchase_line_items + Spree::LineItem + .joins(:order) + .joins(:variant) + .joins(:product) + .where(spree_orders: { state: 'complete', created_at: reporting_period }) + .group('spree_products.id', 'spree_products.name') + .select( + 'SUM(quantity) as purchases', + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_products.id as product_id' + ) end end end diff --git a/app/reports/spree/promotional_cost_report.rb b/app/reports/spree/promotional_cost_report.rb index 4f60ee9..caa22da 100644 --- a/app/reports/spree/promotional_cost_report.rb +++ b/app/reports/spree/promotional_cost_report.rb @@ -1,133 +1,84 @@ module Spree class PromotionalCostReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :promotion_name - HEADERS = { promotion_name: :string, usage_count: :integer, promotion_discount: :integer, promotion_code: :string, promotion_start_date: :date, promotion_end_date: :date } - SEARCH_ATTRIBUTES = { start_date: :promotion_applied_from, end_date: :promotion_applied_till } - SORTABLE_ATTRIBUTES = [:promotion_name, :usage_count, :promotion_discount, :promotion_code, :promotion_start_date, :promotion_end_date] + HEADERS = { promotion_name: :string, usage_count: :integer, promotion_discount: :integer, promotion_code: :string, promotion_start_date: :date, promotion_end_date: :date } + SEARCH_ATTRIBUTES = { start_date: :promotion_applied_from, end_date: :promotion_applied_till } + SORTABLE_ATTRIBUTES = [:promotion_name, :usage_count, :promotion_discount, :promotion_code, :promotion_start_date, :promotion_end_date] - def no_pagination? - true - end + class Result < Spree::Report::TimedResult + charts PromotionalCostChart, UsageCountChart - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + def build_empty_observations + super + @_promotions = @results.collect { |result| result['promotion_name'] }.uniq + @observations = @_promotions.collect do |promotion_name| + @observations.collect do |observation| + _d_observation = observation.dup + _d_observation.promotion_name = promotion_name + _d_observation.usage_count = 0 + _d_observation + end + end.flatten + end - def generate(options = {}) - adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. - join(:spree_promotion_actions___promotion_actions, id: :source_id). - join(:spree_promotions___promotions, id: :promotion_id). - where(adjustments__source_type: "Spree::PromotionAction"). - where(adjustments__created_at: @start_date..@end_date). #filter by params - select{[ - Sequel.as(abs(:amount), :promotion_discount), - Sequel.as(:promotions__id, :promotions_id), - :promotions__name___promotion_name, - :promotions__code___promotion_code, - Sequel.as(DATE_FORMAT(promotions__starts_at,'%d %b %y'), :promotion_start_date), - Sequel.as(DATE_FORMAT(promotions__expires_at,'%d %b %y'), :promotion_end_date), - Sequel.as(MONTHNAME(:adjustments__created_at), :month_name), - Sequel.as(YEAR(:adjustments__created_at), :year), - Sequel.as(MONTH(:adjustments__created_at), :number) - ]} + class Observation < Spree::Report::TimedObservation + observation_fields [ + :promotion_name, :usage_count, + :promotion_discount, :promotion_code, + :promotion_start_date, :promotion_end_date + ] - group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. - group(:months_name, :promotions_id). - order(:year, :number). - select{[ - number, - promotion_name, - year, - promotion_code, - promotion_start_date, - promotion_end_date, - Sequel.as(concat(month_name, ' ', year), :months_name), - Sequel.as(SUM(promotion_discount), :promotion_discount), - Sequel.as(count(:promotions_id), :usage_count), - promotions_id - ]} - grouped_by_promotion = group_by_months.all.group_by { |record| record[:promotion_name] } - data = [] - grouped_by_promotion.each_pair do |promotion_name, collection| - data << fill_missing_values({ promotion_discount: 0, usage_count: 0, promotion_name: promotion_name }, collection) - end - @data = data.flatten - end + def promotion_start_date + @promotion_start_date.present? ? @promotion_start_date.to_date.strftime("%B %d %Y") : "-" + end - def group_by_promotion_name - @grouped_by_promotion_name ||= @data.group_by { |record| record[:promotion_name] } - end + def promotion_end_date + @promotion_end_date.present? ? @promotion_end_date.to_date.strftime("%B %d %Y") : "-" + end - def chart_data - { - months_name: group_by_promotion_name.first.try(:second).try(:map) { |record| record[:months_name] }, - collection: group_by_promotion_name - } - end + def promotion_discount + @promotion_discount.to_f.abs + end - def chart_json - { - chart: true, - charts: [ - promotional_cost_chart_json, - usage_count_chart_json - ] - } + def describes?(result, time_scale) + result['promotion_name'] == promotion_name && super + end + end end - def promotional_cost_chart_json - { - id: 'promotional-cost', - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: "Promotional Cost" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Value($)' } - }, - tooltip: { valuePrefix: '$' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: chart_data[:collection].map { |key, value| { type: 'column', name: key, data: value.map { |r| r[:promotion_discount].to_f } } } - } - } + def report_query + Spree::Report::QueryFragments + .from_subquery(eligible_promotions) + .group(*time_scale_columns, :promotion_id, :promotion_name, + :promotion_code, :promotion_start_date, :promotion_end_date) + .order(*time_scale_columns_to_s) + .project( + *time_scale_columns, + 'promotion_name', + 'promotion_code', + 'promotion_start_date', + 'promotion_end_date', + 'SUM(promotion_discount) as promotion_discount', + 'COUNT(promotion_id) as usage_count', + 'promotion_id' + ) end - def usage_count_chart_json - { - id: 'promotion-usage-count', - json: { - chart: { type: 'spline' }, - title: { - useHTML: true, - text: "Promotion Usage Count" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Count' } - }, - tooltip: { valuePrefix: '#' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: chart_data[:collection].map { |key, value| { name: key, data: value.map { |r| r[:usage_count].to_i } } } - } - } + private def eligible_promotions + Spree::PromotionAction + .joins(:promotion) + .joins(:adjustment) + .where(spree_adjustments: { created_at: reporting_period }) + .select( + 'spree_promotions.starts_at as promotion_start_date', + 'spree_promotions.expires_at as promotion_end_date', + 'spree_adjustments.amount as promotion_discount', + 'spree_promotions.id as promotion_id', + 'spree_promotions.name as promotion_name', + 'spree_promotions.code as promotion_code', + *time_scale_selects('spree_adjustments') + ) end - def select_columns(dataset) - dataset - end end end diff --git a/app/reports/spree/promotional_cost_report/promotional_cost_chart.rb b/app/reports/spree/promotional_cost_report/promotional_cost_chart.rb new file mode 100644 index 0000000..c67cb23 --- /dev/null +++ b/app/reports/spree/promotional_cost_report/promotional_cost_chart.rb @@ -0,0 +1,37 @@ +class Spree::PromotionalCostReport::PromotionalCostChart + attr_accessor :time, :series + + def initialize(result) + @grouped_by_promotion = result.observations.group_by(&:promotion_name) + @time_dimension = result.time_dimension + self.time = [] + self.time = @grouped_by_promotion.values.first.collect { |observation_value| observation_value.send(@time_dimension) } if @grouped_by_promotion.first.present? + self.series = @grouped_by_promotion.collect { |promotion, values| { type: 'column', name: promotion, data: values.collect(&:promotion_discount) } } + end + + def to_h + { + id: 'promotional-cost', + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: "Promotional Cost" + }, + xAxis: { categories: time }, + yAxis: { + title: { text: 'Value($)' } + }, + tooltip: { valuePrefix: '$' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: series + } + } + end + +end diff --git a/app/reports/spree/promotional_cost_report/usage_count_chart.rb b/app/reports/spree/promotional_cost_report/usage_count_chart.rb new file mode 100644 index 0000000..f775cc6 --- /dev/null +++ b/app/reports/spree/promotional_cost_report/usage_count_chart.rb @@ -0,0 +1,41 @@ +class Spree::PromotionalCostReport::UsageCountChart + + attr_accessor :time, :series + + def initialize(result) + @grouped_by_promotion = result.observations.group_by(&:promotion_name) + @time_dimension = result.time_dimension + self.time = [] + if @grouped_by_promotion.values.first.present? + self.time = @grouped_by_promotion.values.first.collect { |observation_value| observation_value.send(@time_dimension) } + end + self.series = @grouped_by_promotion.collect { |promotion, values| { type: 'column', name: promotion, data: values.collect(&:usage_count) } } + end + + + def to_h + { + id: 'promotion-usage-count', + json: { + chart: { type: 'spline' }, + title: { + useHTML: true, + text: "Promotion Usage Count" + }, + xAxis: { categories: time }, + yAxis: { + title: { text: 'Count' } + }, + tooltip: { valuePrefix: '#' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: series + } + } + end + +end diff --git a/app/reports/spree/report.rb b/app/reports/spree/report.rb index eefc2aa..bcc4259 100644 --- a/app/reports/spree/report.rb +++ b/app/reports/spree/report.rb @@ -1,29 +1,71 @@ module Spree class Report - attr_accessor :sortable_attribute, :sortable_type + attr_accessor :sortable_attribute, :sortable_type, :total_records, + :records_per_page, :current_page, :paginate, :search, :reporting_period + alias_method :sort_direction, :sortable_type + alias_method :paginate?, :paginate - def no_pagination? + + TIME_SCALES = [:hourly, :daily, :monthly, :yearly] + + def paginated? false end + def pagination_required? + paginated? && paginate? + end + + def deeplink_properties + { + deeplinked: false + } + end + + def self.deeplink(template_for_headers = {}) + define_method :deeplink_properties do + { deeplinked: true }.merge(template_for_headers) + end + end + def generate(options = {}) - raise 'Please define this method in inherited class' + self.class::Result.new do |report| + report.start_date = @start_date + report.end_date = @end_date + report.time_scale = @time_scale + report.report = self + end end - def initialize(options) - @search = options.fetch(:search, {}) - start_date = @search[:start_date] - @start_date = start_date.present? ? Date.parse(start_date) : Date.new(Date.current.year) - end_date = @search[:end_date] - # 1.day is added to date so that we can get current date records - # since date consider time at midnight - @end_date = (end_date.present? ? Date.parse(end_date) : Date.new(Date.current.year, 12, 30)) + 1.day + def initialize(options) + self.search = options.fetch(:search, {}) + self.records_per_page = options[:records_per_page] + self.current_page = options[:offset] + self.paginate = options[:paginate] + extract_reporting_period + determine_report_time_scale + if self.class::SORTABLE_ATTRIBUTES.present? + set_sortable_attributes(options, self.class::DEFAULT_SORTABLE_ATTRIBUTE) + end end def header_sorted?(header) - sortable_attribute.eql?(header) + sortable_attribute.present? && sortable_attribute.eql?(header) + end + + def get_results + query = + if pagination_required? + paginated_report_query + else + report_query + end + + query = query.order(active_record_sort) if sortable_attribute.present? + query_sql = query.to_sql + r = ActiveRecord::Base.connection.exec_query(query_sql) end def set_sortable_attributes(options, default_sortable_attribute) @@ -31,32 +73,59 @@ def set_sortable_attributes(options, default_sortable_attribute) self.sortable_attribute = options[:sort] ? options[:sort][:attribute].to_sym : default_sortable_attribute end - def sortable_sequel_expression - sortable_type.eql?(:desc) ? Sequel.desc(sortable_attribute) : Sequel.asc(sortable_attribute) + def active_record_sort + "#{ sortable_attribute } #{ sortable_type }" end - def fill_missing_values(default_object, incomplete_result_set) - complete_result_set = [] - year_month_list = (@start_date..@end_date).map{ |date| [date.year, date.month] }.uniq - year_month_list.each do |year_month| - index = incomplete_result_set.index { |obj| obj[:year] == year_month.first && obj[:number] == year_month.second } - if index - complete_result_set.push(incomplete_result_set[index]) - else - filling_object = default_object.merge({ year: year_month.first, number: year_month.second, months_name: [Date::MONTHNAMES[year_month.second], year_month.first].join(' ') }) - complete_result_set.push(filling_object) - end + def total_records + ActiveRecord::Base.connection.select_value(record_count_query.to_sql) + end + + def total_pages + if pagination_required? + total_pages = total_records / records_per_page + total_pages -= 1 if total_records % records_per_page == 0 + total_pages end - complete_result_set end - def chart_json - { - chart: false, - charts: [] - } + def time_scale_selects(time_scale_on = nil) + QueryTimeScale.select(@time_scale, time_scale_on) + end + + def time_scale_columns + @_time_scale_columns ||= QueryTimeScale.time_scale_columns(@time_scale) + end + + def time_scale_columns_to_s + @_time_scale_columns_to_s ||= time_scale_columns.collect(&:to_s) + end + + def name + @_report_name ||= self.class.to_s.demodulize.underscore.gsub("_report", "") + end + + private def extract_reporting_period + start_date = @search[:start_date] + @start_date = start_date.present? ? Date.parse(start_date) : Date.current.beginning_of_year + end_date = @search[:end_date] + @end_date = (end_date.present? ? Date.parse(end_date).next_day : Date.current.end_of_year) + self.reporting_period = (@start_date.beginning_of_day)..(@end_date.end_of_day) end + private def determine_report_time_scale + @time_scale = + case (@end_date - @start_date).to_i + when 0..1 + :hourly + when 1..60 + :daily + when 61..600 + :monthly + else + :yearly + end + end end end diff --git a/app/reports/spree/report/chart.rb b/app/reports/spree/report/chart.rb new file mode 100644 index 0000000..57d3ce8 --- /dev/null +++ b/app/reports/spree/report/chart.rb @@ -0,0 +1,11 @@ +module Spree + class Report + module Chart + extend ActiveSupport::Concern + + def chart_json + self.class::Chart.new(self, time_dimension).to_h + end + end + end +end diff --git a/app/reports/spree/report/date_slicer.rb b/app/reports/spree/report/date_slicer.rb new file mode 100644 index 0000000..d472b66 --- /dev/null +++ b/app/reports/spree/report/date_slicer.rb @@ -0,0 +1,61 @@ +module Spree::Report::DateSlicer + def self.slice_into(start_date, end_date, time_scale, klass) + case time_scale + when :hourly + slice_hours_into(start_date, end_date, klass) + when :daily + slice_days_into(start_date, end_date, klass) + when :monthly + slice_months_into(start_date, end_date, klass) + when :yearly + slice_years_into(start_date, end_date, klass) + end + end + + def self.slice_hours_into(start_date, end_date, klass) + current_date = start_date + slices = [] + while current_date < end_date + slices << (0..23).collect do |hour| + obj = klass.new + obj.date = current_date + obj.hour = hour + obj + end + current_date = current_date.next_day + end + slices.flatten + end + + def self.slice_days_into(start_date, end_date, klass) + current_date = start_date + slices = [] + while current_date < end_date + obj = klass.new + obj.date = current_date + slices << obj + current_date = current_date.next_day + end + slices + end + + def self.slice_months_into(start_date, end_date, klass) + current_date = start_date + slices = [] + while current_date < end_date + obj = klass.new + obj.date = current_date + slices << obj + current_date = current_date.end_of_month.next_day + end + slices + end + + def self.slice_years_into(start_date, end_date, klass) + (start_date.year..end_date.year).collect do |year| + obj = klass.new + obj.date = Date.new(year).end_of_year + obj + end + end +end diff --git a/app/reports/spree/report/observation.rb b/app/reports/spree/report/observation.rb new file mode 100644 index 0000000..f4432e4 --- /dev/null +++ b/app/reports/spree/report/observation.rb @@ -0,0 +1,49 @@ +class Spree::Report::Observation + + def initialize + set_defaults + end + + class << self + def observation_fields(records) + case records + when Hash + build_from_hash(records) + else + build_from_list(records) + end + end + + def build_from_hash(records) + build_from_list(records.keys) + + define_method :set_defaults do + records.keys.each do |key| + self.send("#{ key }=", records[key]) + end + end + end + + def build_from_list(records) + attr_accessor *records + + define_method :populate do |result| + records.each do |record| + record_name = record.to_s + self.send("#{ record }=", result[record_name]) if result[record_name] + end + end + + define_method :set_defaults do + end + + define_method :observations_to_h do + records.inject({}) { |acc, record| acc[record] = self.send(record); acc } + end + end + end + + def to_h + observations_to_h + end +end diff --git a/app/reports/spree/report/query_fragments.rb b/app/reports/spree/report/query_fragments.rb new file mode 100644 index 0000000..ac3397a --- /dev/null +++ b/app/reports/spree/report/query_fragments.rb @@ -0,0 +1,45 @@ +module Spree::Report::QueryFragments + def self.from_subquery(subquery, as: 'results') + Arel::SelectManager.new(Arel.sql("(#{subquery.to_sql}) as #{ as }")) + end + + def self.from_join(subquery1, subquery2, join_expr) + Arel::SelectManager.new(Arel.sql("((#{ subquery1.to_sql }) as q1 JOIN (#{ subquery2.to_sql }) as q2 ON #{ join_expr })")) + end + + def self.from_union(subquery1, subquery2, as: 'results') + Arel::SelectManager.new(Arel.sql("((#{ subquery1.to_sql }) UNION (#{ subquery2.to_sql })) as #{ as }")) + end + + def self.year(column, as='year') + extract_from_date(:year, column, as) + end + + def self.month(column, as='month') + extract_from_date(:month, column, as) + end + + def self.week(column, as='week') + extract_from_date(:week, column, as) + end + + def self.day(column, as='day') + extract_from_date(:day, column, as) + end + + def self.hour(column, as='hour') + extract_from_date(:hour, column, as) + end + + def self.extract_from_date(part, column, as) + "EXTRACT(#{ part } from #{ column }) AS #{ as }" + end + + def self.if_null(val, default_val) + Arel::Nodes::NamedFunction.new('COALESCE', [val, default_val]) + end + + def self.sum(node) + Arel::Nodes::NamedFunction.new('SUM', [node]) + end +end diff --git a/app/reports/spree/report/query_time_scale.rb b/app/reports/spree/report/query_time_scale.rb new file mode 100644 index 0000000..1b139a0 --- /dev/null +++ b/app/reports/spree/report/query_time_scale.rb @@ -0,0 +1,19 @@ +module Spree::Report::QueryTimeScale + def self.select(time_scale, time_scale_on) + db_col_name = time_scale_on.present? ? "#{ time_scale_on }.created_at" : "created_at" + time_scale_columns(time_scale).collect { |time_scale_column| ::Spree::Report::QueryFragments.public_send(time_scale_column, db_col_name) } + end + + def self.time_scale_columns(time_scale) + case time_scale + when :hourly + [:day, :hour] + when :daily + [:month, :day] + when :monthly + [:year, :month] + when :yearly + [:year] + end + end +end diff --git a/app/reports/spree/report/result.rb b/app/reports/spree/report/result.rb new file mode 100644 index 0000000..d3e7de7 --- /dev/null +++ b/app/reports/spree/report/result.rb @@ -0,0 +1,100 @@ +module Spree + class Report + class Result + attr_accessor :start_date, :end_date, :time_scale, :report + attr_reader :observations + + def initialize + yield self + build_report_observations + end + + def build_report_observations + query_results + populate_observations + end + + def query_results + @results = report.get_results + end + + def populate_observations + @observations = @results.collect do |result| + _observation = self.class::Observation.new + _observation.populate(result) + _observation + end + end + + + def to_h + { + deeplink: report.deeplink_properties, + total_pages: report.total_pages, + per_page: report.records_per_page, + pagination_required: report.pagination_required?, + headers: headers, + search_attributes: search_attributes, + stats: observations.collect(&:to_h), + chart_json: chart_json + } + end + + def chart_json + { + chart: false, + charts: [] + } + end + + def self.charts(*report_charts) + define_method :chart_json do + { + chart: true, + charts: report_charts.collect { |report_chart| report_chart.new(self).to_h }.flatten + } + end + end + + def search_attributes + report.class::SEARCH_ATTRIBUTES.transform_values { |value| value.to_s.humanize } + end + + def total_pages # O indexed + if report.pagination_required? + total_pages = report.total_records / report.records_per_page + if report.total_records % report.records_per_page == 0 + total_pages -= 1 + end + total_pages + end + end + + def headers + report.class::HEADERS.keys.collect do |header| + header_description = { + name: Spree.t(header.to_sym, scope: [:insight, report.name]), + value: header, + type: report.class::HEADERS[header], + sortable: header.in?(report.class::SORTABLE_ATTRIBUTES) + } + header_description[:sorted] = report.sort_direction if report.header_sorted?(header) + header_description + end + end + + def time_dimension + case time_scale + when :hourly + :hour_name + when :daily + :day_name + when :monthly + :month_name + when :yearly + :year + end + end + end + end +end diff --git a/app/reports/spree/report/timed_observation.rb b/app/reports/spree/report/timed_observation.rb new file mode 100644 index 0000000..81b889c --- /dev/null +++ b/app/reports/spree/report/timed_observation.rb @@ -0,0 +1,47 @@ +class Spree::Report::TimedObservation < Spree::Report::Observation + + extend Forwardable + + attr_accessor :date, :hour, :reportable_keys + + def_delegators :date, :day, :month, :year + + def initialize + super + self.hour = 0 + end + + def describes?(result, time_scale) + case time_scale + when :hourly + result['hour'] == hour && result['day'] == day + when :daily + result['day'] == day && result['month'] == month + when :monthly + result['month'] == month && result['year'] == year + when :yearly + result['year'] == year + end + end + + def month_name + Date::MONTHNAMES[month] + end + + def hour_name + if hour == 23 + return "23:00 - 00:00" + else + return "#{ hour }:00 - #{ hour + 1 }:00" + end + end + + def day_name + "#{ day } #{ month_name }" + end + + def to_h + super.merge({day_name: day_name, month_name: month_name, year: year, hour_name: hour_name}) + end + +end diff --git a/app/reports/spree/report/timed_result.rb b/app/reports/spree/report/timed_result.rb new file mode 100644 index 0000000..a2fac3b --- /dev/null +++ b/app/reports/spree/report/timed_result.rb @@ -0,0 +1,48 @@ +module Spree + class Report + class TimedResult < Result + + def build_report_observations + query_results + build_empty_observations + populate_observations + end + + def build_empty_observations + @observations = Spree::Report::DateSlicer.slice_into(start_date, end_date, time_scale, self.class::Observation) + end + + def populate_observations + observation_iter = @observations.each + current_observation = @observations.present? ? observation_iter.next : nil + @results.each do |result| + if current_observation.present? + begin + until current_observation.describes? result, time_scale + current_observation = observation_iter.next + end + + current_observation.populate(result) + current_observation = observation_iter.next + rescue StopIteration + break + end + end + end + end + + def headers + [time_headers] + super + end + + private def time_headers + { + name: Spree.t(time_dimension, scope: [:admin]), + value: time_dimension, + type: :string, + sortable: false + } + end + end + end +end diff --git a/app/reports/spree/returned_products_report.rb b/app/reports/spree/returned_products_report.rb index f8657ee..7f41997 100644 --- a/app/reports/spree/returned_products_report.rb +++ b/app/reports/spree/returned_products_report.rb @@ -1,32 +1,37 @@ module Spree class ReturnedProductsReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { sku: :string, product_name: :string, return_count: :integer } - SEARCH_ATTRIBUTES = { start_date: :product_returned_from, end_date: :product_returned_till } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :return_count] + HEADERS = { sku: :string, product_name: :string, return_count: :integer } + SEARCH_ATTRIBUTES = { start_date: :product_returned_from, end_date: :product_returned_till } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :return_count] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:sku, :product_name, :return_count, :product_slug] - def generate - SpreeAdminInsights::ReportDb[:spree_return_authorizations]. - join(:spree_return_items, return_authorization_id: :spree_return_authorizations__id). - join(:spree_inventory_units, spree_inventory_units__id: :inventory_unit_id). - join(:spree_variants, spree_variants__id: :variant_id). - join(:spree_products, id: :product_id). - where(spree_return_items__created_at: @start_date..@end_date). - group(:variant_id). - order(sortable_sequel_expression) + def sku + @sku.presence || @product_name + end + end end - def select_columns(dataset) - dataset.select{[ - spree_products__name.as(product_name), - Sequel.as(IF(STRCMP(spree_variants__sku, ''), spree_variants__sku, spree_products__name), :sku), - Sequel.as(count(:variant_id), :return_count) - ]} + def report_query + Spree::ReturnAuthorization + .joins(:return_items) + .joins(:inventory_units) + .joins(:variants) + .joins(:products) + .where(spree_return_items: { created_at: reporting_period }) + .group('spree_variants.id', 'spree_products.name', 'spree_products.slug', 'spree_variants.sku') + .select( + 'spree_products.name as product_name', + 'spree_products.slug as product_slug', + 'spree_variants.sku as sku', + 'COUNT(spree_variants.id) as return_count' + ) end + end end diff --git a/app/reports/spree/sales_performance_report.rb b/app/reports/spree/sales_performance_report.rb index abf8b6c..c6fc4a6 100644 --- a/app/reports/spree/sales_performance_report.rb +++ b/app/reports/spree/sales_performance_report.rb @@ -1,206 +1,107 @@ module Spree class SalesPerformanceReport < Spree::Report - HEADERS = { months_name: :string, sale_price: :integer, cost_price: :integer, promotion_discount: :integer, profit_loss: :integer, profit_loss_percent: :integer } - SEARCH_ATTRIBUTES = { start_date: :orders_created_from, end_date: :orders_created_till } + HEADERS = { sale_price: :integer, cost_price: :integer, promotion_discount: :integer, profit_loss: :integer, profit_loss_percent: :integer } + SEARCH_ATTRIBUTES = { start_date: :orders_created_from, end_date: :orders_created_till } SORTABLE_ATTRIBUTES = [] - def no_pagination? - true - end + class Result < Spree::Report::TimedResult + charts ProfitLossChart, ProfitLossPercentChart, SaleCostPriceChart - def generate(options = {}) - order_join_line_item = SpreeAdminInsights::ReportDb[:spree_orders___orders]. - exclude(completed_at: nil). - where(orders__created_at: @start_date..@end_date). #filter by params - join(:spree_line_items___line_items, order_id: :id). - group(:line_items__order_id). - select{[ - Sequel.as(SUM(IFNULL(line_items__cost_price, line_items__price) * line_items__quantity), :cost_price), - Sequel.as(orders__item_total, :sale_price), - Sequel.as(orders__item_total - SUM(IFNULL(line_items__cost_price, line_items__price) * line_items__quantity), :profit_loss), - Sequel.as(MONTHNAME(:orders__created_at), :month_name), - Sequel.as(MONTH(:orders__created_at), :number), - Sequel.as(YEAR(:orders__created_at), :year) - ]} + class Observation < Spree::Report::TimedObservation + observation_fields cost_price: 0, sale_price: 0, profit_loss: 0, profit_loss_percent: 0, promotion_discount: 0 - group_by_months = SpreeAdminInsights::ReportDb[order_join_line_item]. - group(:months_name). - order(:year, :number). - select{[ - number, - Sequel.as(IFNULL(year, 2016), :year), - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - Sequel.as(IFNULL(SUM(sale_price), 0), :sale_price), - Sequel.as(IFNULL(SUM(cost_price), 0), :cost_price), - Sequel.as(IFNULL(SUM(profit_loss), 0), :profit_loss), - Sequel.as((IFNULL(SUM(profit_loss), 0) / SUM(cost_price)) * 100, :profit_loss_percent), - Sequel.as(0, :promotion_discount) - ]} + def cost_price + @cost_price.to_f + end - adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. - where(adjustments__source_type: "Spree::PromotionAction"). - where(adjustments__created_at: @start_date..@end_date). #filter by params - select{[ - Sequel.as(abs(:amount), :promotion_discount), - Sequel.as(MONTHNAME(:adjustments__created_at), :month_name), - Sequel.as(YEAR(:adjustments__created_at), :year), - Sequel.as(MONTH(:adjustments__created_at), :number) - ]} + def sale_price + @sale_price.to_f + end - promotions_group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. - group(:months_name). - order(:year, :number). - select{[ - number, - year, - Sequel.as(concat(month_name, ' ', year), :months_name), - Sequel.as(0, :sale_price), - Sequel.as(0, :cost_price), - Sequel.as(SUM(promotion_discount) * (-1), :profit_loss), - Sequel.as(0, :profit_loss_percent), - Sequel.as(SUM(promotion_discount), :promotion_discount) - ]} + def profit_loss + @profit_loss.to_f + end - union_stats = SpreeAdminInsights::ReportDb[group_by_months.union(promotions_group_by_months)]. - group(:months_name). - order(:year, :number). - select{[ - number, - year, - months_name, - Sequel.as(SUM(sale_price), :sale_price), - Sequel.as(SUM(cost_price), :cost_price), - Sequel.as(SUM(profit_loss), :profit_loss), - Sequel.as(ROUND(ABS((SUM(profit_loss) / SUM(cost_price))) * 100, 2), :profit_loss_percent), - Sequel.as(SUM(promotion_discount), :promotion_discount) - ]} - fill_missing_values({ cost_price: 0, sale_price: 0, profit_loss: 0, profit_loss_percent: 0, promotion_discount: 0 }, union_stats.all) - end + def profit_loss_percent + return (profit_loss * 100 / cost_price).round(2) unless cost_price.zero? + 0.0 + end - def select_columns(dataset) - dataset - end + def promotion_discount + @promotion_discount.to_f + end + end - def chart_json - { - chart: true, - charts: [ - profit_loss_chart_json, - profit_loss_percent_chart_json, - sale_cost_price_chart_json - ] - } end - # extract it in report.rb - def chart_data - unless @data - @data = Hash.new {|h, k| h[k] = [] } - generate.each do |object| - object.each_pair do |key, value| - @data[key].push(value) - end - end - end - @data + private def report_query + Spree::Report::QueryFragments + .from_union(order_with_line_items_grouped_by_time, promotions_grouped_by_time) + .group(*time_scale_columns_to_s) + .order(*time_scale_columns) + .project( + *time_scale_columns, + 'SUM(sale_price) as sale_price', + 'SUM(cost_price) as cost_price', + 'SUM(profit_loss) as profit_loss', + 'SUM(promotion_discount) as promotion_discount' + ) end - # ---------------------------------------------------- Graph Jsons -------------------------------------------------- + private def promotions_grouped_by_time + Spree::Report::QueryFragments + .from_subquery(promotion_adjustments_with_time) + .group(*time_scale_columns_to_s, 'sale_price', 'cost_price') + .order(*time_scale_columns) + .project( + *time_scale_columns, + '0 as sale_price', + '0 as cost_price', + 'SUM(promotion_discount) * -1 as profit_loss', + 'SUM(promotion_discount) as promotion_discount' + ) + end - def profit_loss_chart_json - { - id: 'profit-loss', - json: { - title: { - useHTML: true, - text: "Profit/Loss" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Value($)' } - }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: [ - { - name: 'Profit Loss', - tooltip: { valuePrefix: '$' }, - data: chart_data[:profit_loss].map(&:to_f) - } - ] - } - } + private def promotion_adjustments_with_time + Spree::Adjustment + .promotion + .where(created_at: reporting_period) + .select( + 'abs(amount) as promotion_discount', + *time_scale_selects('spree_adjustments') + ) end - def profit_loss_percent_chart_json - { - id: 'profit-loss', - json: { - title: { - useHTML: true, - text: "Profit/Loss %" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Percentage(%)' } - }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: [ - { - name: 'Profit Loss Percent(%)', - tooltip: { valueSuffix: '%' }, - data: chart_data[:profit_loss_percent].map(&:to_f) - } - ] - } - } + private def order_with_line_items_grouped_by_time + order_with_line_items_ar = Arel::Table.new(:order_with_line_items) + zero = Arel::Nodes.build_quoted(0.0) + Spree::Report::QueryFragments + .from_subquery(order_with_line_items, as: :order_with_line_items) + .group(*time_scale_columns_to_s) + .order(*time_scale_columns) + .project( + *time_scale_columns, + Spree::Report::QueryFragments.if_null(Spree::Report::QueryFragments.sum(order_with_line_items_ar[:sale_price]), zero).as('sale_price'), + Spree::Report::QueryFragments.if_null(Spree::Report::QueryFragments.sum(order_with_line_items_ar[:cost_price]), zero).as('cost_price'), + Spree::Report::QueryFragments.if_null(Spree::Report::QueryFragments.sum(order_with_line_items_ar[:profit_loss]), zero).as('profit_loss'), + '0 as promotion_discount' + ) end - def sale_cost_price_chart_json - { - id: 'sale-price', - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: "Sales Performance %" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Value($)' } - }, - tooltip: { valuePrefix: '$' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: [ - { - name: 'Sale Price', - data: chart_data[:sale_price].map(&:to_f) - }, - { - name: 'Cost Price', - data: chart_data[:cost_price].map(&:to_f) - }, - { - name: 'Promotional Cost', - data: chart_data[:promotion_discount].map(&:to_f) - } - ] - } - } + private def order_with_line_items + line_item_ar = Spree::LineItem.arel_table + Spree::Order + .where.not(completed_at: nil) + .where(created_at: reporting_period) + .joins(:line_items) + .group('spree_orders.id', *time_scale_columns_to_s) + .select( + *time_scale_selects('spree_orders'), + "spree_orders.item_total as sale_price", + "SUM(#{ Spree::Report::QueryFragments.if_null(line_item_ar[:cost_price], line_item_ar[:price]).to_sql } * spree_line_items.quantity) as cost_price", + "(spree_orders.item_total - SUM(#{ Spree::Report::QueryFragments.if_null(line_item_ar[:cost_price], line_item_ar[:price]).to_sql } * spree_line_items.quantity)) as profit_loss" + ) end + end end diff --git a/app/reports/spree/sales_performance_report/profit_loss_chart.rb b/app/reports/spree/sales_performance_report/profit_loss_chart.rb new file mode 100644 index 0000000..c366f56 --- /dev/null +++ b/app/reports/spree/sales_performance_report/profit_loss_chart.rb @@ -0,0 +1,37 @@ +class Spree::SalesPerformanceReport::ProfitLossChart + def initialize(result) + time_dim = result.time_dimension + @time_series = result.observations.collect(&time_dim) + @data = result.observations.collect(&:profit_loss) + end + + def to_h + { + id: 'profit-loss', + json: { + title: { + useHTML: true, + text: "Profit/Loss" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Value($)' } + }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: [ + { + name: 'Profit Loss', + tooltip: { valuePrefix: '$' }, + data: @data + } + ] + } + } + + end +end diff --git a/app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb b/app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb new file mode 100644 index 0000000..bef653d --- /dev/null +++ b/app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb @@ -0,0 +1,36 @@ +class Spree::SalesPerformanceReport::ProfitLossPercentChart + def initialize(result) + time_dim = result.time_dimension + @time_series = result.observations.collect(&time_dim) + @data = result.observations.collect(&:profit_loss_percent) + end + + def to_h + { + id: 'profit-loss-percent', + json: { + title: { + useHTML: true, + text: "Profit/Loss %" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Percentage(%)' } + }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: [ + { + name: 'Profit Loss Percent(%)', + tooltip: { valueSuffix: '%' }, + data: @data + } + ] + } + } + end +end diff --git a/app/reports/spree/sales_performance_report/sale_cost_price_chart.rb b/app/reports/spree/sales_performance_report/sale_cost_price_chart.rb new file mode 100644 index 0000000..dd2e63a --- /dev/null +++ b/app/reports/spree/sales_performance_report/sale_cost_price_chart.rb @@ -0,0 +1,48 @@ +class Spree::SalesPerformanceReport::SaleCostPriceChart + def initialize(result) + time_dim = result.time_dimension + @time_series = result.observations.collect(&time_dim) + @sale_price = result.observations.collect(&:sale_price) + @cost_price = result.observations.collect(&:cost_price) + @promotion_discount = result.observations.collect(&:promotion_discount) + end + + def to_h + { + id: 'sale-price', + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: "Sales Performance %" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Value($)' } + }, + tooltip: { valuePrefix: '$' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: [ + { + name: 'Sale Price', + data: @sale_price + }, + { + name: 'Cost Price', + data: @cost_price + }, + { + name: 'Promotional Cost', + data: @promotion_discount + } + ] + } + } + + end +end diff --git a/app/reports/spree/sales_tax_report.rb b/app/reports/spree/sales_tax_report.rb index 21a8e56..9079d37 100644 --- a/app/reports/spree/sales_tax_report.rb +++ b/app/reports/spree/sales_tax_report.rb @@ -1,89 +1,64 @@ module Spree class SalesTaxReport < Spree::Report - HEADERS = { months_name: :string, zone_name: :string, sales_tax: :integer } - SEARCH_ATTRIBUTES = { start_date: :taxation_from, end_date: :taxation_till } + HEADERS = { zone_name: :string, sales_tax: :integer } + SEARCH_ATTRIBUTES = { start_date: :taxation_from, end_date: :taxation_till } SORTABLE_ATTRIBUTES = [] - def no_pagination? - true - end + class Result < Spree::Report::TimedResult + charts MonthlySalesTaxComparisonChart + + def build_empty_observations + super + @_zones = @results.collect { |r| r['zone_name'] }.uniq + @observations = @_zones.collect do |zone| + @observations.collect do |observation| + _d_observation = observation.dup + _d_observation.zone_name = zone + _d_observation.sales_tax = 0 + _d_observation + end + end.flatten + end - def generate(options = {}) - adjustments_with_month_name = SpreeAdminInsights::ReportDb[:spree_adjustments___adjustments]. - join(:spree_tax_rates___tax_rates, id: :source_id). - join(:spree_zones___zones, id: :zone_id). - where(adjustments__source_type: "Spree::TaxRate", adjustments__adjustable_type: "Spree::LineItem"). - where(adjustments__created_at: @start_date..@end_date). #filter by params - select{[ - Sequel.as(abs(adjustments__amount), :sales_tax), - Sequel.as(:zones__id, :zone_id), - :zones__name___zone_name, - Sequel.as(MONTHNAME(:adjustments__created_at), :month_name), - Sequel.as(YEAR(:adjustments__created_at), :year), - Sequel.as(MONTH(:adjustments__created_at), :number) - ]} + class Observation < Spree::Report::TimedObservation + observation_fields [:zone_name, :sales_tax] - group_by_months = SpreeAdminInsights::ReportDb[adjustments_with_month_name]. - group(:months_name, :zone_id). - order(:year, :number). - select{[ - number, - zone_name, - year, - Sequel.as(concat(month_name, ' ', year), :months_name), - Sequel.as(SUM(sales_tax), :sales_tax) - ]} - grouped_by_zone = group_by_months.all.group_by { |record| record[:zone_name] } - data = [] - grouped_by_zone.each_pair do |zone_name, collection| - data << fill_missing_values({ sales_tax: 0, zone_name: zone_name }, collection) + def describes?(result, time_scale) + (zone_name == result['zone_name']) && super + end + + def sales_tax + @sales_tax.to_f + end end - @data = data.flatten end - def group_by_zone_name - @grouped_by_zone_name ||= @data.group_by { |record| record[:zone_name] } - end - def chart_data - { - months_name: group_by_zone_name.first.try(:second).try(:map) { |record| record[:months_name] }, - collection: group_by_zone_name - } + def report_query + Spree::Report::QueryFragments + .from_subquery(tax_adjustments) + .group(*time_scale_columns_to_s, 'zone_name') + .order(*time_scale_columns) + .project( + 'zone_name', + *time_scale_columns, + 'SUM(sales_tax) as sales_tax' + ) end - def chart_json - { - chart: true, - charts: [ - { - id: 'sale-tax', - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: "Monthly Sales Tax Comparison" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Value($)' } - }, - tooltip: { valuePrefix: '$' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: chart_data[:collection].map { |key, value| { type: 'column', name: key, data: value.map { |r| r[:sales_tax].to_f } } } - } - } - ] - } + private def tax_adjustments + Spree::TaxRate + .joins(:adjustments) + .joins(:zone) + .where(spree_adjustments: { adjustable_type: 'Spree::LineItem' } ) + .where(spree_adjustments: { created_at: reporting_period }) + .select( + 'spree_adjustments.amount as sales_tax', + 'spree_zones.id as zone_id', + 'spree_zones.name as zone_name', + *time_scale_selects('spree_adjustments') + ) end - def select_columns(dataset) - dataset - end end end diff --git a/app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb b/app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb new file mode 100644 index 0000000..a8a751b --- /dev/null +++ b/app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb @@ -0,0 +1,39 @@ +class Spree::SalesTaxReport::MonthlySalesTaxComparisonChart + def initialize(result) + @time_dimension = result.time_dimension + @grouped_by_zone_name = result.observations.group_by(&:zone_name) + @time_series = [] + if @grouped_by_zone_name.first.present? + @time_series = @grouped_by_zone_name.values.first.collect { |observation| observation.send(@time_dimension) } + end + @chart_series = @grouped_by_zone_name.map { |zone_name, observations| { type: 'column', name: zone_name, data: observations.collect(&:sales_tax) } } + end + + def to_h + { + id: 'sale-tax', + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: "Monthly Sales Tax Comparison" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Value($)' } + }, + tooltip: { valuePrefix: '$' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: @chart_series + } + } + + end + + +end diff --git a/app/reports/spree/shipping_cost_report.rb b/app/reports/spree/shipping_cost_report.rb index 8edef5b..7097e90 100644 --- a/app/reports/spree/shipping_cost_report.rb +++ b/app/reports/spree/shipping_cost_report.rb @@ -1,119 +1,89 @@ module Spree class ShippingCostReport < Spree::Report - HEADERS = { months_name: :string, name: :string, shipping_charge: :integer, revenue: :integer, shipping_cost_percentage: :integer } - SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date } + HEADERS = { name: :string, shipping_charge: :integer, revenue: :integer, shipping_cost_percentage: :integer } + SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date } SORTABLE_ATTRIBUTES = [] - def no_pagination? - true - end - - def generate(options = {}) - order_join_shipments = SpreeAdminInsights::ReportDb[:spree_orders___orders]. - exclude(completed_at: nil). - join(:spree_shipments___shipments, order_id: :id). - where(orders__created_at: @start_date..@end_date). #filter by params - select{[ - Sequel.as(shipments__id, :shipment_id), - Sequel.as(orders__shipment_total, :shipping_charge), - Sequel.as(shipments__order_id, :order_id), - Sequel.as(orders__total, :order_total), - Sequel.as(MONTHNAME(:orders__created_at), :month_name), - Sequel.as(MONTH(:orders__created_at), :number), - Sequel.as(YEAR(:orders__created_at), :year) - ]}.as(:order_shipment) + class Result < Spree::Report::TimedResult + charts ShippingCostDistributionChart - order_shipment_join_shipment_rates = SpreeAdminInsights::ReportDb[order_join_shipments]. - join(:spree_shipping_rates___shipping_rates, shipment_id: :order_shipment__shipment_id). - where(selected: true). - select{[ - order_id, - shipping_charge, - order_total, - shipping_method_id, - month_name, - number, - year, - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - ]}.as(:order_shipment_rates) + def build_empty_observations + super + @_shipping_methods = @results.collect { |r| r['name'] }.uniq + @observations = @_shipping_methods.collect do |shipping_method| + @observations.collect do |observation| + _d_observation = observation.dup + _d_observation.name = shipping_method + _d_observation.revenue = 0 + _d_observation.shipping_charge = 0 + _d_observation.shipping_cost_percentage = 0 + _d_observation + end + end.flatten + end - revenue_table = SpreeAdminInsights::ReportDb[order_shipment_join_shipment_rates]. - group(:months_name). - select{[ - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - Sequel.as(SUM(order_total), :revenue), - order_id - ]} + class Observation < Spree::Report::TimedObservation + observation_fields [:name, :shipping_charge, :revenue, :shipping_cost_percentage] - group_by_months = SpreeAdminInsights::ReportDb[order_shipment_join_shipment_rates]. - join(:spree_shipping_methods, id: :order_shipment_rates__shipping_method_id). - join(revenue_table, months_name: :order_shipment_rates__months_name). - group(:months_name, :spree_shipping_methods__id). - order(:year, :number). - select{[ - order_shipment_rates__order_id, - spree_shipping_methods__id, - Sequel.as(SUM(shipping_charge), :shipping_charge), - revenue, - shipping_method_id, - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - Sequel.as(ROUND((SUM(shipping_charge) / revenue) * 100, 2), :shipping_cost_percentage), - number, - year, - name - ]} + def describes?(result, time_scale) + (name = result['name']) && super + end - grouped_by_method_name = group_by_months.all.group_by { |record| record[:name] } - data = [] - grouped_by_method_name.each_pair do |name, collection| - data << fill_missing_values({ shipping_charge: 0, revenue: 0, name: name, shipping_cost_percentage: 0 }, collection) + def shipping_cost_percentage + ((@shipping_charge.to_f * 100) / @revenue.to_f).round(2) + end end - @data = data.flatten end - def group_by_method_name - @grouped_by_method_name ||= @data.group_by { |record| record[:name] } - end + def report_query + ar_shipping_methods = Arel::Table.new(:spree_shipping_methods) + ar_subquery_with_rates = Arel::Table.new(:shipment_with_rates) - def chart_data - { - months_name: group_by_method_name.first.try(:second).try(:map) { |record| record[:months_name] }, - collection: group_by_method_name - } + Spree::Report::QueryFragments + .from_subquery(shipment_with_rates, as: 'shipment_with_rates') + .join(ar_shipping_methods) + .on(ar_shipping_methods[:id].eq(ar_subquery_with_rates[:shipping_method_id])) + .project( + *time_scale_columns, + ar_shipping_methods[:id], + 'revenue', + 'shipping_charge', + 'shipping_method_id', + 'name' + ) end - def chart_json - { - chart: true, - charts: [ - { - id: 'shipping-cost-percentage-comparison', - json: { - chart: { type: 'spline' }, - title: { - useHTML: true, - text: "Monthly Shipping Comparison" - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Percentage(%)' } - }, - tooltip: { valueSuffix: '%' }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: chart_data[:collection].map { |key, value| { name: key, data: value.map { |r| r[:shipping_cost_percentage].to_f } } } - } - } - ] - } + private def order_with_shipments + Spree::Order + .where.not(completed_at: nil) + .where(completed_at: reporting_period) + .joins(:shipments) + .select( + 'spree_shipments.id as shipment_id', + 'spree_orders.shipment_total as shipping_charge', + 'spree_orders.id as order_id', + 'spree_orders.total as order_total', + *time_scale_selects('spree_orders') + ) end - def select_columns(dataset) - dataset + private def shipment_with_rates + ar_shipping_rates = Arel::Table.new(:spree_shipping_rates) + ar_subquery = Arel::Table.new(:results) + + Spree::Report::QueryFragments.from_subquery(order_with_shipments) + .join(ar_shipping_rates) + .on(ar_shipping_rates[:shipment_id].eq(ar_subquery[:shipment_id])) + .where(ar_shipping_rates[:selected].eq(Arel::Nodes::Quoted.new(true))) + .group(*time_scale_columns, :shipping_method_id) + .order(*time_scale_columns) + .project( + *time_scale_columns, + 'shipping_method_id', + 'SUM(shipping_charge) as shipping_charge', + 'SUM(order_total) as revenue' + ) end + end end diff --git a/app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb b/app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb new file mode 100644 index 0000000..ab9c523 --- /dev/null +++ b/app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb @@ -0,0 +1,38 @@ +class Spree::ShippingCostReport::ShippingCostDistributionChart + + + def initialize(result) + time_dimension = result.time_dimension + @grouped_by_shipping_method = result.observations.group_by(&:name) + @time_series = [] + @time_series = @grouped_by_shipping_method.values.first.collect { |observation_value| observation_value.send(time_dimension) } if @grouped_by_shipping_method.first.present? + @result_series = @grouped_by_shipping_method.collect { |name, observations| { name: name, data: observations.collect(&:shipping_cost_percentage) } } + end + + def to_h + { + id: 'shipping-cost-percentage-comparison', + json: { + chart: { type: 'spline' }, + title: { + useHTML: true, + text: "Monthly Shipping Comparison" + }, + xAxis: { categories: @time_series }, + yAxis: { + title: { text: 'Percentage(%)' } + }, + tooltip: { valueSuffix: '%' }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: @result_series + } + } + + end + +end diff --git a/app/reports/spree/trending_search_report.rb b/app/reports/spree/trending_search_report.rb index 280bfb0..af25b8d 100644 --- a/app/reports/spree/trending_search_report.rb +++ b/app/reports/spree/trending_search_report.rb @@ -1,79 +1,50 @@ module Spree class TrendingSearchReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :occurrences - HEADERS = { searched_term: :string, occurrences: :integer } - SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, keywords_cont: :keyword } - SORTABLE_ATTRIBUTES = [] + HEADERS = { searched_term: :string, occurrences: :integer } + SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, keywords_cont: :keyword } + SORTABLE_ATTRIBUTES = [:occurrences] - def initialize(options) - super - @search_keywords_cont = @search[:keywords_cont].present? ? "%#{ @search[:keywords_cont] }%" : '%' - @sortable_type = :desc if options[:sort].blank? - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) + def paginated? + true end - def generate(options = {}) - top_searches = SpreeAdminInsights::ReportDb[:spree_page_events___page_events]. - where(page_events__activity: 'search'). - where(page_events__created_at: @start_date..@end_date).where(Sequel.ilike(:page_events__search_keywords, @search_keywords_cont)). #filter by params - group(:searched_term). - order(Sequel.desc(:occurrences)). - limit(20) + class Result < Spree::Report::Result + charts FrequencyDistributionPieChart - top_searches + class Observation < Spree::Report::Observation + observation_fields [:searched_term, :occurrences] + end end - def select_columns(dataset) - dataset.select{[ - search_keywords.as(searched_term), - Sequel.as(count(:search_keywords), :occurrences) - ]} + deeplink searched_term: { template: %Q{{%# o['searched_term'] %}} } + + def paginated_report_query + report_query + .take(records_per_page) + .skip(current_page) + end + + def record_count_query + Spree::Report::QueryFragments.from_subquery(report_query).project(Arel.star.count) end - def chart_data - top_searches = select_columns(generate) - total_occurrences = SpreeAdminInsights::ReportDb[top_searches].sum(:occurrences) - SpreeAdminInsights::ReportDb[top_searches]. - select{[ - Sequel.as((occurrences / total_occurrences) * 100, :y), - Sequel.as(searched_term, :name) - ]}.all.map { |obj| obj.merge({ y: obj[:y].to_f })} # to convert percentage into float value from string + def report_query + Spree::Report::QueryFragments.from_subquery(searches) + .project("count(searched_term) as occurrences", "searched_term") + .group("searched_term") end - def chart_json - { - chart: true, - charts: [ - { - name: 'trending-search', - json: { - chart: { type: 'pie' }, - title: { - useHTML: true, - text: "Trending Search Keywords(Top 20)" - }, - tooltip: { - pointFormat: 'Search %: {point.percentage:.1f}%' - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - series: [{ - name: 'Hits', - data: chart_data - }] - } - } - ] - } + private def searches + Spree::PageEvent + .where(activity: 'search') + .where(created_at: reporting_period) + .where(Spree::PageEvent.arel_table[:search_keywords].matches(keyword_search)) + .select("search_keywords as searched_term") end + private def keyword_search + search[:keywords_cont].present? ? "%#{ search[:keywords_cont] }%" : '%' + end end end diff --git a/app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb b/app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb new file mode 100644 index 0000000..f4c0476 --- /dev/null +++ b/app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb @@ -0,0 +1,41 @@ +class Spree::TrendingSearchReport::FrequencyDistributionPieChart + attr_accessor :chart_data + + def initialize(result) + total_occurrences = result.observations.sum(&:occurrences).to_f + self.chart_data = result.observations.collect { |x| { name: x.searched_term, y: x.occurrences/total_occurrences } } + end + + def to_h + { + + name: 'trending-search', + json: { + chart: { type: 'pie' }, + title: { + useHTML: true, + text: "Trending Search Keywords(Top 20)" + }, + tooltip: { + pointFormat: 'Search %: {point.percentage:.1f}%' + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: true + } + }, + series: [ + { + name: 'Hits', + data: chart_data + } + ] + } + } + end +end diff --git a/app/reports/spree/unique_purchases_report.rb b/app/reports/spree/unique_purchases_report.rb index 11fcc0a..a934b32 100644 --- a/app/reports/spree/unique_purchases_report.rb +++ b/app/reports/spree/unique_purchases_report.rb @@ -1,33 +1,39 @@ module Spree class UniquePurchasesReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :product_name - HEADERS = { sku: :string, product_name: :string, sold_count: :integer, users: :integer } - SEARCH_ATTRIBUTES = { start_date: :orders_completed_from, end_date: :orders_completed_till } - SORTABLE_ATTRIBUTES = [:product_name, :sku, :sold_count, :users] + HEADERS = { sku: :string, product_name: :string, sold_count: :integer, users: :integer } + SEARCH_ATTRIBUTES = { start_date: :orders_completed_from, end_date: :orders_completed_till } + SORTABLE_ATTRIBUTES = [:product_name, :sku, :sold_count, :users] - def initialize(options) - super - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end + deeplink product_name: { template: %Q{{%# o.product_name %}} } + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:product_name, :product_slug, :sku, :sold_count, :users] - def generate(options = {}) - ::SpreeAdminInsights::ReportDb[:spree_line_items___line_items]. - join(:spree_orders___orders, id: :order_id). - join(:spree_variants___variants, variants__id: :line_items__variant_id). - join(:spree_products___products, products__id: :variants__product_id). - where(orders__state: 'complete'). - where(orders__completed_at: @start_date..@end_date). #filter by params - group(:variant_id). - order(sortable_sequel_expression) + def sku + @sku.presence || @product_name + end + end end - def select_columns(dataset) - dataset.select{[ - Sequel.as(IFNULL(variants__sku, products__name), :sku), - products__name.as(product_name), - sum(quantity).as(sold_count), - (count(distinct orders__user_id) + count(orders__id) - count(orders__user_id)).as(users) - ]} + def report_query + user_count_sql = '(COUNT(DISTINCT(spree_orders.user_id)) + COUNT(spree_orders.id) - COUNT(spree_orders.user_id))' + purchases_by_variant = + Spree::LineItem + .joins(:order) + .joins(:variant) + .joins(:product) + .where(spree_orders: { state: 'complete', completed_at: reporting_period }) + .group('variant_id', 'spree_variants.sku', 'spree_products.slug', 'spree_products.name') + .select( + 'spree_variants.sku as sku', + 'spree_products.slug as product_slug', + 'spree_products.name as product_name', + 'SUM(quantity) as sold_count', + "#{ user_count_sql } as users" + ) end + end end diff --git a/app/reports/spree/user_pool_report.rb b/app/reports/spree/user_pool_report.rb index 2063d77..3f7bebd 100644 --- a/app/reports/spree/user_pool_report.rb +++ b/app/reports/spree/user_pool_report.rb @@ -1,133 +1,66 @@ module Spree class UserPoolReport < Spree::Report - DEFAULT_SORTABLE_ATTRIBUTE = :orders__completed_at - HEADERS = { months_name: :string, guest_users: :integer, active_users: :integer, new_sign_ups: :integer } - SEARCH_ATTRIBUTES = { start_date: :users_created_from, end_date: :users_created_till } + HEADERS = { guest_users: :integer, active_users: :integer, new_sign_ups: :integer } + SEARCH_ATTRIBUTES = { start_date: :users_created_from, end_date: :users_created_till } SORTABLE_ATTRIBUTES = [] - def no_pagination? - true - end - - def generate(options = {}) - # order of column is important when we take union of two tables - new_sign_ups = SpreeAdminInsights::ReportDb[:spree_users___users]. - where(users__created_at: @start_date..@end_date). - select{[ - id.as(:user_id), - Sequel.as(YEAR(:users__created_at), :year), - Sequel.as(MONTHNAME(:users__created_at), :month_name), - Sequel.as(MONTH(:users__created_at), :number) - ]} - - group_new_sign_ups_by_months = SpreeAdminInsights::ReportDb[new_sign_ups]. - group(:months_name). - order(:year, :number). - select{[ - number, - year, - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - Sequel.as(0, :guest_users), - Sequel.as(0, :active_users), - Sequel.as(IFNULL(COUNT(user_id), 0), :new_sign_ups) - ]} + class Result < Spree::Report::TimedResult + charts DistributionColumnChart - vistors = SpreeAdminInsights::ReportDb[:spree_page_events___page_events]. - where(page_events__created_at: @start_date..@end_date). - select{[ - Sequel.as(YEAR(:page_events__created_at), :year), - Sequel.as(MONTHNAME(:page_events__created_at), :month_name), - Sequel.as(MONTH(:page_events__created_at), :number), - Sequel.as(actor_id, :user), - Sequel.as(session_id, :session) - ]} - - visitors_by_months = SpreeAdminInsights::ReportDb[vistors]. - group(:months_name). - order(:year, :number). - select{[ - number, - year, - Sequel.as(concat(month_name, ' ', IFNULL(year, 2016)), :months_name), - Sequel.as((COUNT(DISTINCT session) - COUNT(DISTINCT user)), :guest_users), - Sequel.as(COUNT(DISTINCT user), :active_users), - Sequel.as(0, :new_sign_ups) - ]} + class Observation < Spree::Report::TimedObservation + observation_fields active_users: 0, guest_users: 0, new_sign_ups: 0 + end + end + def report_query + Report::QueryFragments + .from_union(grouped_sign_ups, grouped_visitors) + .group(*time_scale_columns) + .order(*time_scale_columns_to_s) + .project( + *time_scale_columns, + 'SUM(active_users) as active_users', + 'SUM(guest_users) as guest_users', + 'SUM(new_sign_ups) as new_sign_ups' + ) + end - union_of_stats = group_new_sign_ups_by_months.union(visitors_by_months) + private def grouped_sign_ups + sign_ups = Spree::User.where(created_at: reporting_period).select(:id, *time_scale_selects) - union_stats = SpreeAdminInsights::ReportDb[union_of_stats]. - group(:months_name). - order(:year, :number). - select{[ - months_name, - year, - number, - Sequel.as(SUM(:guest_users), :guest_users), - Sequel.as(SUM(:active_users), :active_users), - Sequel.as(SUM(:new_sign_ups), :new_sign_ups) - ]} - fill_missing_values({guest_users: 0, active_users: 0, new_sign_ups: 0}, union_stats.all) + Report::QueryFragments.from_subquery(sign_ups) + .group(*time_scale_columns, 'guest_users', 'active_users') + .order(*time_scale_columns_to_s) + .project( + *time_scale_columns, + '0 as guest_users', + '0 as active_users', + 'COUNT(id) as new_sign_ups' + ) end - def select_columns(dataset) - dataset + private def grouped_visitors + guest_count_sql = '(COUNT(DISTINCT(session)) - COUNT(DISTINCT(user)))' + Report::QueryFragments.from_subquery(visitors) + .group(*time_scale_columns, 'new_sign_ups') + .order(*time_scale_columns_to_s) + .project( + *time_scale_columns, + "#{ guest_count_sql } as guest_users", + 'COUNT(DISTINCT(user)) as active_users', + '0 as new_sign_ups' + ) end - # extract it in report.rb - def chart_data - unless @data - @data = Hash.new {|h, k| h[k] = [] } - generate.each do |object| - object.each_pair do |key, value| - @data[key].push(value) - end - end - end - @data + private def visitors + Spree::PageEvent + .where(created_at: reporting_period) + .select( + *time_scale_selects, + 'actor_id as user', + 'session_id as session' + ) end - def chart_json - { - chart: true, - charts: [ - { - id: 'user-pool', - json: { - chart: { type: 'column' }, - title: { - useHTML: true, - text: 'User Pool' - }, - xAxis: { categories: chart_data[:months_name] }, - yAxis: { - title: { text: 'Count' } - }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: [ - { - name: Spree.t('user_pool.new_sign_ups'), - data: chart_data[:new_sign_ups].map(&:to_i) - }, - { - name: Spree.t('user_pool.active_users'), - data: chart_data[:active_users].map(&:to_i) - }, - { - name: Spree.t('user_pool.guest_users'), - data: chart_data[:guest_users].map(&:to_i) - } - ] - } - } - ] - } - end end end diff --git a/app/reports/spree/user_pool_report/distribution_column_chart.rb b/app/reports/spree/user_pool_report/distribution_column_chart.rb new file mode 100644 index 0000000..4e94a31 --- /dev/null +++ b/app/reports/spree/user_pool_report/distribution_column_chart.rb @@ -0,0 +1,65 @@ +class Spree::UserPoolReport::DistributionColumnChart + def initialize(result) + @chart_data = { + active_users: [], + guest_users: [], + new_sign_ups: [] + } + @time_dimension = result.time_dimension + @chart_data[@time_dimension] = [] + @result = result + process_chart_data + end + + def process_chart_data + chart_keys = @chart_data.keys + @result.observations.each do |observation| + chart_keys.each do |key| + @chart_data[key] << observation.public_send(key) + end + end + end + + def to_h + { + id: 'user-pool', + json: { + chart: { type: 'column' }, + title: { + useHTML: true, + text: %Q( + User Pool + + + ) + }, + xAxis: { categories: @chart_data[@time_dimension] }, + yAxis: { + title: { text: 'Count' } + }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0 + }, + series: [ + { + name: Spree.t('user_pool.new_sign_ups'), + data: @chart_data[:new_sign_ups].map(&:to_i) + }, + { + name: Spree.t('user_pool.active_users'), + data: @chart_data[:active_users].map(&:to_i) + }, + { + name: Spree.t('user_pool.guest_users'), + data: @chart_data[:guest_users].map(&:to_i) + } + ] + } + } + end +end diff --git a/app/reports/spree/users_not_converted_report.rb b/app/reports/spree/users_not_converted_report.rb index cb836df..b7608bc 100644 --- a/app/reports/spree/users_not_converted_report.rb +++ b/app/reports/spree/users_not_converted_report.rb @@ -1,29 +1,48 @@ module Spree class UsersNotConvertedReport < Spree::Report - DEFAULT_SORTABLE_ATTRIBUTE = :orders__completed_at - HEADERS = { user_email: :string, signup_date: :date } - SEARCH_ATTRIBUTES = { start_date: :users_created_from, end_date: :users_created_till, email_cont: :email } - SORTABLE_ATTRIBUTES = [:user_email, :signup_date] - - def initialize(options) - super - @sortable_type = :desc if options[:sort].blank? - @email_cont = @search[:email_cont].present? ? "%#{ @search[:email_cont] }%" : '%' + DEFAULT_SORTABLE_ATTRIBUTE = :user_email + HEADERS = { user_email: :string, signup_date: :date } + SEARCH_ATTRIBUTES = { start_date: :users_created_from, end_date: :users_created_till, email_cont: :email } + SORTABLE_ATTRIBUTES = [:user_email, :signup_date] + + def paginated? + true end - def generate(options = {}) - SpreeAdminInsights::ReportDb[:spree_users___users]. - left_join(:spree_orders___orders, user_id: :id). - where(orders__completed_at: nil, orders__number: nil). - where(users__created_at: @start_date..@end_date).where(Sequel.ilike(:users__email, @email_cont)). #filter by params - order(sortable_sequel_expression) + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:user_email, :signup_date] + + def signup_date + @signup_date.to_date.strftime("%B %d, %Y") + end + end end - def select_columns(dataset) - dataset.select{[ - users__email.as(user_email), - users__created_at.as(signup_date) - ]} + def paginated_report_query + report_query + .limit(records_per_page) + .offset(current_page) end + + def record_count_query + Spree::Report::QueryFragments.from_subquery(report_query).project(Arel.star.count) + end + + def report_query + Spree::User + .where(created_at: reporting_period) + .where(Spree::User.arel_table[:email].matches(email_search)) + .left_joins(:spree_orders) + .where(spree_orders: { completed_at: nil, number: nil }) + .select( + "spree_users.email as user_email", + "spree_users.created_at as signup_date") + end + + private def email_search + search[:email_cont].present? ? "%#{ search[:email_cont] }%" : '%' + end + end end diff --git a/app/reports/spree/users_who_have_not_recently_purchased_report.rb b/app/reports/spree/users_who_have_not_recently_purchased_report.rb deleted file mode 100644 index ccd121a..0000000 --- a/app/reports/spree/users_who_have_not_recently_purchased_report.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Spree - class UsersWhoHaveNotRecentlyPurchasedReport < Spree::Report - DEFAULT_SORTABLE_ATTRIBUTE = :user_email - HEADERS = { user_email: :string, last_purchase_date: :date, last_purchased_order_number: :string } - SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, email_cont: :email } - SORTABLE_ATTRIBUTES = [:user_email, :last_purchase_date] - - def initialize(options) - super - @email_cont = @search[:email_cont].present? ? "%#{ @search[:email_cont] }%" : '%' - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) - end - - def generate(options = {}) - all_orders_with_users = SpreeAdminInsights::ReportDb[:spree_users___users]. - left_join(:spree_orders___orders, user_id: :id). - where(Sequel.~(orders__completed_at: nil), Sequel.~(orders__completed_at: @start_date..@end_date)). - where(Sequel.ilike(:users__email, @email_cont)). - order(Sequel.desc(:orders__completed_at)). - select( - :users__email___user_email, - :orders__number___last_purchased_order_number, - :orders__completed_at___last_purchase_date - ).as(:all_orders_with_users) - - SpreeAdminInsights::ReportDb[all_orders_with_users]. - group(:all_orders_with_users__user_email). - order(sortable_sequel_expression) - end - - def select_columns(dataset) - dataset.select_all - end - end -end diff --git a/app/reports/spree/users_who_recently_purchased_report.rb b/app/reports/spree/users_who_recently_purchased_report.rb index eb72ac1..ab1324d 100644 --- a/app/reports/spree/users_who_recently_purchased_report.rb +++ b/app/reports/spree/users_who_recently_purchased_report.rb @@ -1,40 +1,69 @@ module Spree class UsersWhoRecentlyPurchasedReport < Spree::Report DEFAULT_SORTABLE_ATTRIBUTE = :user_email - HEADERS = { user_email: :string, purchase_count: :integer, last_purchase_date: :date, last_purchased_order_number: :string } - SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, email_cont: :email } - SORTABLE_ATTRIBUTES = [:user_email, :purchase_count, :last_purchase_date] - - def initialize(options) - super - @email_cont = @search[:email_cont].present? ? "%#{ @search[:email_cont] }%" : '%' - set_sortable_attributes(options, DEFAULT_SORTABLE_ATTRIBUTE) + HEADERS = { user_email: :string, purchase_count: :integer, last_purchase_date: :date, last_purchased_order_number: :string } + SEARCH_ATTRIBUTES = { start_date: :start_date, end_date: :end_date, email_cont: :email } + SORTABLE_ATTRIBUTES = [:user_email, :purchase_count, :last_purchase_date] + + def paginated? + true + end + + class Result < Spree::Report::Result + class Observation < Spree::Report::Observation + observation_fields [:user_email, :last_purchased_order_number, :last_purchase_date, :purchase_count] + + def last_purchase_date + @last_purchase_date.to_date.strftime("%B %d, %Y") + end + end + end + + def record_count_query + Spree::Report::QueryFragments.from_subquery(report_query).project(Arel.star.count) + end + + def report_query + Spree::Report::QueryFragments + .from_subquery(all_orders_with_users) + .project( + "user_email", + "last_purchased_order_number", + "last_purchase_date", + "COUNT(user_email) as purchase_count") + .group( + "user_email", + "last_purchased_order_number", + "last_purchase_date" + ) + end + + + def paginated_report_query + report_query + .take(records_per_page) + .skip(current_page) end - def generate(options = {}) - all_orders_with_users = SpreeAdminInsights::ReportDb[:spree_users___users]. - left_join(:spree_orders___orders, user_id: :id). - where(orders__completed_at: @start_date..@end_date). - where(Sequel.ilike(:users__email, @email_cont)). - order(Sequel.desc(:orders__completed_at)). - select( - :users__email___user_email, - :orders__number___last_purchased_order_number, - :orders__completed_at___last_purchase_date, - ).as(:all_orders_with_users) - - SpreeAdminInsights::ReportDb[all_orders_with_users]. - group(:all_orders_with_users__user_email). - order(sortable_sequel_expression) + private def all_orders_with_users + Spree::User + .where(Spree::User.arel_table[:email].matches(email_search)) + .left_joins(:spree_orders) + .where(spree_orders: { completed_at: reporting_period }) + .order("spree_orders.completed_at desc") + .select( + "spree_users.email as user_email", + "spree_orders.number as last_purchased_order_number", + "spree_orders.completed_at as last_purchase_date" + ).group( + 'user_email', + "spree_orders.number", + "spree_orders.completed_at" + ) end - def select_columns(dataset) - dataset.select{[ - all_orders_with_users__user_email, - all_orders_with_users__last_purchased_order_number, - all_orders_with_users__last_purchase_date, - count(all_orders_with_users__user_email).as(purchase_count) - ]} + private def email_search + search[:email_cont].present? ? "%#{ search[:email_cont] }%" : '%' end end end diff --git a/app/services/spree/report_generation_service.rb b/app/services/spree/report_generation_service.rb index 8580e0d..8f64604 100644 --- a/app/services/spree/report_generation_service.rb +++ b/app/services/spree/report_generation_service.rb @@ -3,71 +3,55 @@ class ReportGenerationService REPORTS = { finance_analysis: [ - :payment_method_transactions, :payment_method_transactions_conversion_rate, - :sales_performance, :shipping_cost, :sales_tax - ], + :payment_method_transactions, :payment_method_transactions_conversion_rate, + :shipping_cost, :sales_tax, :sales_performance + ], product_analysis: [ - :cart_additions, :cart_removals, :cart_updations, - :product_views, :product_views_to_cart_additions, - :product_views_to_purchases, :unique_purchases, - :best_selling_products, :returned_products - ], - promotion_analysis: [:promotional_cost, :annual_promotional_cost], + :best_selling_products, :cart_additions, :cart_removals, :cart_updations, + :product_views, :product_views_to_cart_additions, + :product_views_to_purchases, :unique_purchases, + :returned_products + ], + promotion_analysis: [:promotional_cost], trending_search_analysis: [:trending_search], - user_analysis: [:user_pool, :users_not_converted, :users_who_recently_purchased] + user_analysis: [:user_pool, :users_not_converted, :users_who_recently_purchased], } def self.generate_report(report_name, options) klass = Spree.const_get((report_name.to_s + '_report').classify) resource = klass.new(options) dataset = resource.generate - total_records = resource.select_columns(dataset).count - if resource.no_pagination? - result_set = dataset - else - result_set = resource.select_columns(dataset.limit(options['records_per_page'], options['offset'])).all - end - options['no_pagination'] = resource.no_pagination?.to_s unless options['no_pagination'] == 'true' - [headers(klass, resource, report_name), result_set, total_pages(total_records, options['records_per_page'], options['no_pagination']), search_attributes(klass), resource.chart_json, resource] end - def self.download(options = {}, headers, stats) + def self.download(report, options = {}) + headers = report.headers + stats = report.observations ::CSV.generate(options) do |csv| csv << headers.map { |head| head[:name] } stats.each do |record| - csv << headers.map { |head| record[head[:value]] } + csv << headers.map { |head| record.public_send(head[:value]) } end end end - def self.search_attributes(klass) - search_attributes = {} - klass::SEARCH_ATTRIBUTES.each do |key, value| - search_attributes[key] = value.to_s.humanize - end - search_attributes + def self.report_exists?(type, name) + REPORTS.key?(type) && REPORTS[type].include?(name) end - def self.total_pages(total_records, records_per_page, no_pagination) - if no_pagination != 'true' - total_pages = total_records / records_per_page - if total_records % records_per_page == 0 - total_pages -= 1 - end - total_pages - end + def self.reports_for_type(type) + REPORTS[type] end - def self.headers(klass, resource, report_name) - klass::HEADERS.keys.map do |header| - { - name: Spree.t(header.to_sym, scope: [:insight, report_name]), - value: header, - sorted: resource.try(:header_sorted?, header) ? resource.sortable_type.to_s : nil, - type: klass::HEADERS[header], - sortable: header.in?(klass::SORTABLE_ATTRIBUTES) - } - end + def self.default_report_type + REPORTS.keys.first + end + + def self.register_report_category(category) + REPORTS[category] = [] + end + + def self.register_report(category, report_name) + REPORTS[category] << report_name end end diff --git a/app/views/spree/admin/insights/download.pdf.erb b/app/views/spree/admin/insights/download.pdf.erb index 3d3a3f2..ea0f973 100644 --- a/app/views/spree/admin/insights/download.pdf.erb +++ b/app/views/spree/admin/insights/download.pdf.erb @@ -10,16 +10,16 @@ - <% @headers.each do |_header_| %> + <% @report.headers.each do |_header_| %> <% end %> - <% @stats.each do |stat| %> + <% @report.observations.each do |stat| %> - <% @headers.each do |head|%> - + <% @report.headers.each do |head|%> + <% end %> <% end %> diff --git a/app/views/spree/admin/templates/insights/_show.template b/app/views/spree/admin/templates/insights/_show.template index 9c2c7bd..257e397 100644 --- a/app/views/spree/admin/templates/insights/_show.template +++ b/app/views/spree/admin/templates/insights/_show.template @@ -8,7 +8,7 @@ {%= o['headers'][i].name %} {% if(o['headers'][i].sorted != undefined) { %} - + {% } %} {% } else { %} {%= o['headers'][i].name %} @@ -22,18 +22,22 @@ {% for(var j = 0; j < o['headers'].length; j++) { %} {% } %} @@ -41,5 +45,4 @@ {% } %}
<%= Spree.t(_header_[:value], scope: [:insight, @report_name], default: _header_[:name]) %>
<%= stat[head[:value]] %><%= stat.public_send(head[:value]) %>
- {% if (o['headers'][j].value == 'profit_loss') { %} + {% if (o['headers'][j].value == 'profit_loss' || o['headers'][j].value == 'profit_loss_percent') { %} {%= Math.abs(o['stats'][i][o['headers'][j].value] || 0) %} {% if (o['stats'][i][o['headers'][j].value] > 0) { %} - + {% } %} {% if (o['stats'][i][o['headers'][j].value] < 0) { %} - + {% } %} {% } else if (o['headers'][j].type == 'integer') { %} {%= o['stats'][i][o['headers'][j].value] || 0 %} {% } else { %} - {%= o['stats'][i][o['headers'][j].value] || '-' %} + {% if (o['deeplink']['deeplinked'] && o['deeplink'][o['headers'][j].value]) { %} + {%# tmpl(o['deeplink'][o['headers'][j].value]['template'], o['stats'][i]) %} + {% } else { %} + {%= o['stats'][i][o['headers'][j].value] || '-' %} + {% } %} {% } %}
- diff --git a/config/locales/en.yml b/config/locales/en.yml index bb160b3..9e25347 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,10 @@ en: spree: + admin: + day_name: Day + hour_name: Hour + month_name: Month + year: Year finance_analysis: Finance Analysis product_analysis: Product Analysis promotion_analysis: Promotion Analysis diff --git a/lib/spree_admin_insights/engine.rb b/lib/spree_admin_insights/engine.rb index f838f44..4fd9cfb 100644 --- a/lib/spree_admin_insights/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -1,7 +1,6 @@ module SpreeAdminInsights class Engine < Rails::Engine require 'spree/core' - require 'sequel' require 'wicked_pdf' require 'csv' @@ -22,13 +21,6 @@ def self.activate config.to_prepare &method(:activate).to_proc config.after_initialize do - configuration_hash = (ActiveRecord::Base.configurations[Rails.env] || - Rails.configuration.database_configuration[Rails.env] - ).to_h - configuration_hash.merge!({ 'adapter' => 'sqlite' }) if(configuration_hash['adapter'] == 'sqlite3') - - # Connect to applications DB using ruby's Sequel wrapper - ::SpreeAdminInsights::ReportDb = Sequel.connect(configuration_hash) end end end diff --git a/spree_admin_insights.gemspec b/spree_admin_insights.gemspec index 36728ab..eec0d51 100644 --- a/spree_admin_insights.gemspec +++ b/spree_admin_insights.gemspec @@ -35,7 +35,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'shoulda-matchers', '~> 2.6.2' s.add_development_dependency 'spree_backend', spree_version s.add_development_dependency 'spree_frontend', spree_version - s.add_dependency 'sequel', '~> 4.32.0' s.add_dependency 'wicked_pdf', '~> 1.0.6' s.add_dependency 'wkhtmltopdf-binary', '~> 0.9.9.3' end From cd975827777f6a1e529e03bdf4d8daf336af4c0f Mon Sep 17 00:00:00 2001 From: Nimish Date: Tue, 16 May 2017 14:50:31 +0530 Subject: [PATCH 10/12] Some naming changes, initializer and configuration 1. Standardize on report_category instead of using type and category alternatively. 2. Add initializer to configure list of reports to be used. 3. Reduce responsibility of report generation service to just generate reports --- .../spree_admin_insights/report_loader.js | 2 +- .../spree/admin/insights_controller.rb | 16 ++++---- app/reports/spree/report/configuration.rb | 40 +++++++++++++++++++ .../spree/report_generation_service.rb | 39 ++---------------- app/views/spree/admin/insights/index.html.erb | 8 ++-- .../insights/search/_search_form.html.erb | 4 +- .../admin/shared/_insights_side_menu.html.erb | 4 +- .../admin/templates/insights/_show.template | 2 +- .../install/install_generator.rb | 6 ++- .../install/spree_admin_insights.rb | 22 ++++++++++ lib/spree_admin_insights.rb | 10 +++++ lib/spree_admin_insights/engine.rb | 2 +- 12 files changed, 99 insertions(+), 56 deletions(-) create mode 100644 app/reports/spree/report/configuration.rb create mode 100644 lib/generators/spree_admin_insights/install/spree_admin_insights.rb diff --git a/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js index de473b9..5684a9a 100644 --- a/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js +++ b/app/assets/javascripts/spree/backend/spree_admin_insights/report_loader.js @@ -136,7 +136,7 @@ ReportLoader.prototype.fetchChartData = function(url, $selectedOption) { $(object).removeClass('col-md-3').addClass('col-md-2'); }); } - _this.perPageSelector.data('url', data['request_path'] + '?type=' + data['report_type']); + _this.perPageSelector.data('url', data['request_path'] + '?report_category=' + data['report_category']); _this.setDownloadLinksPath(); _this.searcherObject.refreshSearcher($selectedOption, data); _this.paginatorObject.refreshPaginator(data); diff --git a/app/controllers/spree/admin/insights_controller.rb b/app/controllers/spree/admin/insights_controller.rb index fb737b6..c3648d3 100644 --- a/app/controllers/spree/admin/insights_controller.rb +++ b/app/controllers/spree/admin/insights_controller.rb @@ -49,32 +49,32 @@ def download private def ensure_report_exists @report_name = params[:id].to_sym - unless ReportGenerationService.report_exists?(get_reports_type, @report_name) + unless ReportGenerationService.report_exists?(get_report_category, @report_name) redirect_to admin_insights_path, alert: Spree.t(:not_found, scope: [:reports]) end end def load_reports - @reports = ReportGenerationService.reports_for_type(get_reports_type) + @reports = ReportGenerationService.reports_for_category(get_report_category) end def shared_data { current_page: params[:page] || 0, - report_type: params[:type], + report_category: params[:report_category], request_path: request.path, url: request.url, searched_fields: params[:search], } end - def get_reports_type - params[:type] = if params[:type] - params[:type].to_sym + def get_report_category + params[:report_category] = if params[:report_category] + params[:report_category].to_sym else - session[:report_category].try(:to_sym) || ReportGenerationService.default_report_type + session[:report_category].try(:to_sym) || ReportGenerationService.default_report_category end - session[:report_category] = params[:type] + session[:report_category] = params[:report_category] end def set_reporting_period diff --git a/app/reports/spree/report/configuration.rb b/app/reports/spree/report/configuration.rb new file mode 100644 index 0000000..30dc757 --- /dev/null +++ b/app/reports/spree/report/configuration.rb @@ -0,0 +1,40 @@ +class Spree::Report::Configuration + attr_accessor :default_report_category, :default_report + attr_reader :reports + + def initialize + @reports = {} + end + + def register_report_category(category) + @reports[category] = [] + end + + def register_report(category, report_name) + @reports[category] << report_name + end + + def report_exists?(category, name) + @reports.key?(category) && @reports[category].include?(name) + end + + def reports_for_category(category) + if category_exists? category + @reports[category] + else + [] + end + end + + def default_report_category + @default_report_category || @reports.keys.first + end + + def default_report + @default_report || @reports[default_report_category].first + end + + def category_exists?(category) + @reports.key? category + end +end diff --git a/app/services/spree/report_generation_service.rb b/app/services/spree/report_generation_service.rb index 8f64604..a7480a1 100644 --- a/app/services/spree/report_generation_service.rb +++ b/app/services/spree/report_generation_service.rb @@ -1,21 +1,10 @@ module Spree class ReportGenerationService - REPORTS = { - finance_analysis: [ - :payment_method_transactions, :payment_method_transactions_conversion_rate, - :shipping_cost, :sales_tax, :sales_performance - ], - product_analysis: [ - :best_selling_products, :cart_additions, :cart_removals, :cart_updations, - :product_views, :product_views_to_cart_additions, - :product_views_to_purchases, :unique_purchases, - :returned_products - ], - promotion_analysis: [:promotional_cost], - trending_search_analysis: [:trending_search], - user_analysis: [:user_pool, :users_not_converted, :users_who_recently_purchased], - } + class << self + delegate :reports, :report_exists?, :reports_for_category, :default_report_category, to: :configuration + delegate :configuration, to: SpreeAdminInsights + end def self.generate_report(report_name, options) klass = Spree.const_get((report_name.to_s + '_report').classify) @@ -34,25 +23,5 @@ def self.download(report, options = {}) end end - def self.report_exists?(type, name) - REPORTS.key?(type) && REPORTS[type].include?(name) - end - - def self.reports_for_type(type) - REPORTS[type] - end - - def self.default_report_type - REPORTS.keys.first - end - - def self.register_report_category(category) - REPORTS[category] = [] - end - - def self.register_report(category, report_name) - REPORTS[category] << report_name - end - end end diff --git a/app/views/spree/admin/insights/index.html.erb b/app/views/spree/admin/insights/index.html.erb index 4551116..7a47f69 100644 --- a/app/views/spree/admin/insights/index.html.erb +++ b/app/views/spree/admin/insights/index.html.erb @@ -1,14 +1,14 @@ <% content_for :page_title do %> - <%= Spree.t(:heading, type: params[:type].to_s.titleize, scope: :insight) %> + <%= Spree.t(:heading, type: params[:report_category].to_s.titleize, scope: :insight) %> <% end %> <% content_for :title do %> - <%= Spree.t(:heading, type: params[:type].to_s.titleize, scope: :insight) %> + <%= Spree.t(:heading, type: params[:report_category].to_s.titleize, scope: :insight) %> <% end %>
- <%= select_tag :reports, options_for_select(@reports.map { |report_name| [ Spree.t(:title, scope: [:insight, report_name]), report_name, data: { url: admin_insight_url(report_name, type: params[:type]) }] }.unshift(['Select a report', '', data: { url: admin_insights_url(type: params[:type]) }, disabled: true]), @report_name || ''), class: 'select2' %> + <%= select_tag :reports, options_for_select(@reports.map { |report_name| [ Spree.t(:title, scope: [:insight, report_name]), report_name, data: { url: admin_insight_url(report_name, report_category: params[:report_category]) }] }.unshift(['Select a report', '', data: { url: admin_insights_url(report_category: params[:report_category]) }, disabled: true]), @report_name || ''), class: 'select2' %>
<%= content_tag :div, class: 'hidden report-data', data: { report_data: @report_data_json } {} %> @@ -24,7 +24,7 @@ <%= Spree.t(:filter) %> - <%= form_tag form_action(@report_name, params[:type]), id: "quick-search" do %> + <%= form_tag form_action(@report_name, params[:report_category]), id: "quick-search" do %> <%= text_field_tag :quick_search, nil, class: "form-control js-quick-search", placeholder: Spree.t(:quick_search) %> <% end %> diff --git a/app/views/spree/admin/insights/search/_search_form.html.erb b/app/views/spree/admin/insights/search/_search_form.html.erb index 59fe0e6..10866a6 100644 --- a/app/views/spree/admin/insights/search/_search_form.html.erb +++ b/app/views/spree/admin/insights/search/_search_form.html.erb @@ -1,5 +1,5 @@
- <%= form_tag form_action(@report_name, params[:type]), method: :get, id: "filter-search" do %> + <%= form_tag form_action(@report_name, params[:category]), method: :get, id: "filter-search" do %>
@@ -37,5 +37,3 @@ <% end %>
- - diff --git a/app/views/spree/admin/shared/_insights_side_menu.html.erb b/app/views/spree/admin/shared/_insights_side_menu.html.erb index be189a5..b2424fa 100644 --- a/app/views/spree/admin/shared/_insights_side_menu.html.erb +++ b/app/views/spree/admin/shared/_insights_side_menu.html.erb @@ -7,8 +7,8 @@ diff --git a/app/views/spree/admin/templates/insights/_show.template b/app/views/spree/admin/templates/insights/_show.template index 257e397..ab8b7e4 100644 --- a/app/views/spree/admin/templates/insights/_show.template +++ b/app/views/spree/admin/templates/insights/_show.template @@ -5,7 +5,7 @@ {% for(var i = 0; i < o['headers'].length; i++) { %} {% if(o['headers'][i].sortable) { %} - {%= o['headers'][i].name %} + {%= o['headers'][i].name %} {% if(o['headers'][i].sorted != undefined) { %} diff --git a/lib/generators/spree_admin_insights/install/install_generator.rb b/lib/generators/spree_admin_insights/install/install_generator.rb index 38e220b..6a3db49 100644 --- a/lib/generators/spree_admin_insights/install/install_generator.rb +++ b/lib/generators/spree_admin_insights/install/install_generator.rb @@ -1,9 +1,13 @@ module SpreeAdminInsights module Generators class InstallGenerator < Rails::Generators::Base - + source_root(File.expand_path(File.dirname(__FILE__))) class_option :auto_run_migrations, :type => :boolean, :default => false + def copy_initializer + copy_file 'spree_admin_insights.rb', 'config/initializers/spree_admin_insights.rb' + end + def add_javascripts append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_admin_insights\n" append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_admin_insights\n" diff --git a/lib/generators/spree_admin_insights/install/spree_admin_insights.rb b/lib/generators/spree_admin_insights/install/spree_admin_insights.rb new file mode 100644 index 0000000..cc56ad5 --- /dev/null +++ b/lib/generators/spree_admin_insights/install/spree_admin_insights.rb @@ -0,0 +1,22 @@ +reports = { + finance_analysis: [ + :payment_method_transactions, :payment_method_transactions_conversion_rate, + :shipping_cost, :sales_tax, :sales_performance + ], + product_analysis: [ + :best_selling_products, :cart_additions, :cart_removals, :cart_updations, + :product_views, :product_views_to_cart_additions, + :product_views_to_purchases, :unique_purchases, + :returned_products + ], + promotion_analysis: [:promotional_cost, :annual_promotional_cost], + trending_search_analysis: [:trending_search], + user_analysis: [:user_pool, :users_not_converted, :users_who_recently_purchased], +} + +SpreeAdminInsights.configure do |config| + reports.keys.each do |report_category| + config.register_report_category(report_category) + reports[report_category].each { |report| config.register_report(report_category, report) } + end +end diff --git a/lib/spree_admin_insights.rb b/lib/spree_admin_insights.rb index c7af00d..deab5ca 100644 --- a/lib/spree_admin_insights.rb +++ b/lib/spree_admin_insights.rb @@ -1,2 +1,12 @@ require 'spree_core' require 'spree_admin_insights/engine' + +class SpreeAdminInsights + def self.configure + yield configuration if block_given? + end + + def self.configuration + @config ||= Spree::Report::Configuration.new + end +end diff --git a/lib/spree_admin_insights/engine.rb b/lib/spree_admin_insights/engine.rb index 4fd9cfb..8aba154 100644 --- a/lib/spree_admin_insights/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -1,4 +1,4 @@ -module SpreeAdminInsights +class SpreeAdminInsights class Engine < Rails::Engine require 'spree/core' require 'wicked_pdf' From 07d16e299dcf9f8b7d0490fb9b7cc23018a8040c Mon Sep 17 00:00:00 2001 From: Nimish Date: Tue, 16 May 2017 15:04:08 +0530 Subject: [PATCH 11/12] Move config to nested subclass. Remove annual promotional cost report. --- app/services/spree/report_generation_service.rb | 2 +- .../install/spree_admin_insights.rb | 4 ++-- lib/spree_admin_insights.rb | 14 ++++++++------ lib/spree_admin_insights/engine.rb | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/services/spree/report_generation_service.rb b/app/services/spree/report_generation_service.rb index a7480a1..2439db8 100644 --- a/app/services/spree/report_generation_service.rb +++ b/app/services/spree/report_generation_service.rb @@ -3,7 +3,7 @@ class ReportGenerationService class << self delegate :reports, :report_exists?, :reports_for_category, :default_report_category, to: :configuration - delegate :configuration, to: SpreeAdminInsights + delegate :configuration, to: SpreeAdminInsights::Config end def self.generate_report(report_name, options) diff --git a/lib/generators/spree_admin_insights/install/spree_admin_insights.rb b/lib/generators/spree_admin_insights/install/spree_admin_insights.rb index cc56ad5..e1f49ed 100644 --- a/lib/generators/spree_admin_insights/install/spree_admin_insights.rb +++ b/lib/generators/spree_admin_insights/install/spree_admin_insights.rb @@ -9,12 +9,12 @@ :product_views_to_purchases, :unique_purchases, :returned_products ], - promotion_analysis: [:promotional_cost, :annual_promotional_cost], + promotion_analysis: [:promotional_cost], trending_search_analysis: [:trending_search], user_analysis: [:user_pool, :users_not_converted, :users_who_recently_purchased], } -SpreeAdminInsights.configure do |config| +SpreeAdminInsights::Config.configure do |config| reports.keys.each do |report_category| config.register_report_category(report_category) reports[report_category].each { |report| config.register_report(report_category, report) } diff --git a/lib/spree_admin_insights.rb b/lib/spree_admin_insights.rb index deab5ca..9257d09 100644 --- a/lib/spree_admin_insights.rb +++ b/lib/spree_admin_insights.rb @@ -1,12 +1,14 @@ require 'spree_core' require 'spree_admin_insights/engine' -class SpreeAdminInsights - def self.configure - yield configuration if block_given? - end +module SpreeAdminInsights + class Config + def self.configure + yield configuration if block_given? + end - def self.configuration - @config ||= Spree::Report::Configuration.new + def self.configuration + @config ||= Spree::Report::Configuration.new + end end end diff --git a/lib/spree_admin_insights/engine.rb b/lib/spree_admin_insights/engine.rb index 8aba154..4fd9cfb 100644 --- a/lib/spree_admin_insights/engine.rb +++ b/lib/spree_admin_insights/engine.rb @@ -1,4 +1,4 @@ -class SpreeAdminInsights +module SpreeAdminInsights class Engine < Rails::Engine require 'spree/core' require 'wicked_pdf' From 3b859bae77ea21a6cda8754d09fca35d4bbf17ce Mon Sep 17 00:00:00 2001 From: Nimish Date: Wed, 26 Jul 2017 14:15:13 +0530 Subject: [PATCH 12/12] Backport changes for older version of RAILS/AREL For detailed changelog see: https://github.com/vinsol-spree-contrib/spree-admin-insights/pull/33 --- app/reports/spree/report/query_fragments.rb | 6 +++--- app/reports/spree/users_not_converted_report.rb | 2 +- app/reports/spree/users_who_recently_purchased_report.rb | 2 +- spree_admin_insights.gemspec | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/reports/spree/report/query_fragments.rb b/app/reports/spree/report/query_fragments.rb index ac3397a..bd21c26 100644 --- a/app/reports/spree/report/query_fragments.rb +++ b/app/reports/spree/report/query_fragments.rb @@ -1,14 +1,14 @@ module Spree::Report::QueryFragments def self.from_subquery(subquery, as: 'results') - Arel::SelectManager.new(Arel.sql("(#{subquery.to_sql}) as #{ as }")) + Arel::SelectManager.new(Arel::Table.engine, Arel.sql("(#{subquery.to_sql}) as #{ as }")) end def self.from_join(subquery1, subquery2, join_expr) - Arel::SelectManager.new(Arel.sql("((#{ subquery1.to_sql }) as q1 JOIN (#{ subquery2.to_sql }) as q2 ON #{ join_expr })")) + Arel::SelectManager.new(Arel::Table.engine, Arel.sql("((#{ subquery1.to_sql }) as q1 JOIN (#{ subquery2.to_sql }) as q2 ON #{ join_expr })")) end def self.from_union(subquery1, subquery2, as: 'results') - Arel::SelectManager.new(Arel.sql("((#{ subquery1.to_sql }) UNION (#{ subquery2.to_sql })) as #{ as }")) + Arel::SelectManager.new(Arel::Table.engine, Arel.sql("((#{ subquery1.to_sql }) UNION (#{ subquery2.to_sql })) as #{ as }")) end def self.year(column, as='year') diff --git a/app/reports/spree/users_not_converted_report.rb b/app/reports/spree/users_not_converted_report.rb index b7608bc..ad5c4b9 100644 --- a/app/reports/spree/users_not_converted_report.rb +++ b/app/reports/spree/users_not_converted_report.rb @@ -33,7 +33,7 @@ def report_query Spree::User .where(created_at: reporting_period) .where(Spree::User.arel_table[:email].matches(email_search)) - .left_joins(:spree_orders) + .joins("LEFT JOIN spree_orders on spree_orders.user_id = spree_users.id") .where(spree_orders: { completed_at: nil, number: nil }) .select( "spree_users.email as user_email", diff --git a/app/reports/spree/users_who_recently_purchased_report.rb b/app/reports/spree/users_who_recently_purchased_report.rb index ab1324d..cd0b705 100644 --- a/app/reports/spree/users_who_recently_purchased_report.rb +++ b/app/reports/spree/users_who_recently_purchased_report.rb @@ -48,7 +48,7 @@ def paginated_report_query private def all_orders_with_users Spree::User .where(Spree::User.arel_table[:email].matches(email_search)) - .left_joins(:spree_orders) + .joins("LEFT JOIN spree_orders on spree_orders.user_id = spree_users.id") .where(spree_orders: { completed_at: reporting_period }) .order("spree_orders.completed_at desc") .select( diff --git a/spree_admin_insights.gemspec b/spree_admin_insights.gemspec index eec0d51..c843c7f 100644 --- a/spree_admin_insights.gemspec +++ b/spree_admin_insights.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - spree_version = '~> 3.2.0.alpha' + spree_version = '~> 3.1.0' s.add_dependency 'spree_core', spree_version