From 22c08c5bbf16f84b6decb7d7c6b56539969c669a Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 13 Sep 2019 16:41:52 +0200 Subject: [PATCH 01/56] [TPI frontend] Sectors dashboard PoC with chartkick --- Gemfile | 3 +++ Gemfile.lock | 2 ++ app/assets/javascripts/application.js | 2 ++ app/assets/javascripts/companies.js | 2 ++ app/assets/javascripts/sectors.js | 2 ++ app/assets/stylesheets/companies.scss | 3 +++ app/assets/stylesheets/sectors.scss | 3 +++ app/controllers/companies_controller.rb | 3 +++ app/controllers/sectors_controller.rb | 12 ++++++++++++ app/decorators/sector_decorator.rb | 12 ++++++++++++ app/helpers/companies_helper.rb | 2 ++ app/helpers/sectors_helper.rb | 2 ++ app/views/companies/show.html.erb | 2 ++ app/views/layouts/application.html.erb | 1 - app/views/sectors/index.html.erb | 5 +++++ app/views/sectors/show.html.erb | 2 ++ config/routes.rb | 3 +++ package.json | 1 + yarn.lock | 5 +++++ 19 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/companies.js create mode 100644 app/assets/javascripts/sectors.js create mode 100644 app/assets/stylesheets/companies.scss create mode 100644 app/assets/stylesheets/sectors.scss create mode 100644 app/controllers/companies_controller.rb create mode 100644 app/controllers/sectors_controller.rb create mode 100644 app/decorators/sector_decorator.rb create mode 100644 app/helpers/companies_helper.rb create mode 100644 app/helpers/sectors_helper.rb create mode 100644 app/views/companies/show.html.erb create mode 100644 app/views/sectors/index.html.erb create mode 100644 app/views/sectors/show.html.erb diff --git a/Gemfile b/Gemfile index 42eb57c36..89ce364f4 100644 --- a/Gemfile +++ b/Gemfile @@ -30,6 +30,9 @@ gem 'language_list' gem 'simplecov', require: false, group: :test gem 'dotenv-rails' +# TPI +gem 'chartkick' + group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'capybara' diff --git a/Gemfile.lock b/Gemfile.lock index 9bc128f0d..5c24191b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -145,6 +145,7 @@ GEM rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) + chartkick (3.2.1) concurrent-ruby (1.1.5) crass (1.0.4) declarative (0.0.10) @@ -468,6 +469,7 @@ DEPENDENCIES capistrano-rvm capistrano-yarn capybara + chartkick climate_watch_engine (~> 1.4.3)! devise (>= 4.7.1) discard diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 82e6f0f6c..a88b46269 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,4 +13,6 @@ //= require rails-ujs //= require activestorage //= require turbolinks +//= require chartkick +//= require highcharts //= require_tree . diff --git a/app/assets/javascripts/companies.js b/app/assets/javascripts/companies.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/companies.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/sectors.js b/app/assets/javascripts/sectors.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/sectors.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/companies.scss b/app/assets/stylesheets/companies.scss new file mode 100644 index 000000000..412c0f58b --- /dev/null +++ b/app/assets/stylesheets/companies.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Companies controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sectors.scss b/app/assets/stylesheets/sectors.scss new file mode 100644 index 000000000..a0fe8452e --- /dev/null +++ b/app/assets/stylesheets/sectors.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Sectors controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb new file mode 100644 index 000000000..1039c605d --- /dev/null +++ b/app/controllers/companies_controller.rb @@ -0,0 +1,3 @@ +class CompaniesController < ApplicationController + def show; end +end diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb new file mode 100644 index 000000000..ace47f056 --- /dev/null +++ b/app/controllers/sectors_controller.rb @@ -0,0 +1,12 @@ +class SectorsController < ApplicationController + def index + # get companies grouped by latest MQ assessments sector's levels + @companies_data = Company + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .map { |k,v| ["Level #{k}", v.size]} + end + + def show + end +end diff --git a/app/decorators/sector_decorator.rb b/app/decorators/sector_decorator.rb new file mode 100644 index 000000000..1faf03e2d --- /dev/null +++ b/app/decorators/sector_decorator.rb @@ -0,0 +1,12 @@ +class SectorDecorator < Draper::Decorator + delegate_all + + # Define presentation-specific methods here. Helpers are accessed through + # `helpers` (aka `h`). You can override attributes, for example: + # + # def created_at + # helpers.content_tag :span, class: 'time' do + # object.created_at.strftime("%a %m/%d/%y") + # end + # end +end diff --git a/app/helpers/companies_helper.rb b/app/helpers/companies_helper.rb new file mode 100644 index 000000000..099b151b9 --- /dev/null +++ b/app/helpers/companies_helper.rb @@ -0,0 +1,2 @@ +module CompaniesHelper +end diff --git a/app/helpers/sectors_helper.rb b/app/helpers/sectors_helper.rb new file mode 100644 index 000000000..42152977f --- /dev/null +++ b/app/helpers/sectors_helper.rb @@ -0,0 +1,2 @@ +module SectorsHelper +end diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb new file mode 100644 index 000000000..65b8dd114 --- /dev/null +++ b/app/views/companies/show.html.erb @@ -0,0 +1,2 @@ +

Companies#show

+

Find me in app/views/companies/show.html.erb

diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index a8d1f0fd3..63064f505 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -5,7 +5,6 @@ <%= csrf_meta_tags %> <%= csp_meta_tag %> - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb new file mode 100644 index 000000000..ebb0757c4 --- /dev/null +++ b/app/views/sectors/index.html.erb @@ -0,0 +1,5 @@ +

Sectors dashboard

+ +

Management Quality: All sectors

+ +<%= pie_chart @companies_data, donut: true, library: { tooltip: { enabled: false }, title: { text: '123 Companies' }, plotOptions: { pie: { dataLabels: { format: "{point.name}
{point.y} companies
{point.percentage:.1f}%" }} } } %> diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb new file mode 100644 index 000000000..7dc19b9df --- /dev/null +++ b/app/views/sectors/show.html.erb @@ -0,0 +1,2 @@ +

Sectors#show

+<%= pie_chart Company.group(:sector).count %> diff --git a/config/routes.rb b/config/routes.rb index 7381ddc80..8bc3ed499 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,7 @@ Rails.application.routes.draw do + get 'companies/show' + get 'sectors/index' + get 'sectors/show' devise_for :admin_users, ActiveAdmin::Devise.config ActiveAdmin.routes(self) diff --git a/package.json b/package.json index 6ababae67..88f0ea331 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@rails/webpacker": "^4.0.7", + "highcharts": "^7.2.0", "stimulus": "^1.1.1", "trix": "^1.1.1" }, diff --git a/yarn.lock b/yarn.lock index bc2134acf..2ec6f5fe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3103,6 +3103,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highcharts@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/highcharts/-/highcharts-7.2.0.tgz#d6bd00240767cfab67210ead47c6cd015384e74d" + integrity sha512-jOlMzj3oRuqNBoJg+rqHZI9vnlLHipwVfceJr00gAreXu8268UwahEh7yBGI/m1wa6Uc/XoBMIuzMpXUFMvFdg== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" From 2dcf729616c56631c9146aa89ca770b983b94be6 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sat, 14 Sep 2019 08:17:17 +0200 Subject: [PATCH 02/56] add Carbon Performance: All sectors chart --- app/controllers/sectors_controller.rb | 29 ++++++++++++++++++--- app/views/sectors/index.html.erb | 37 +++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index ace47f056..20bf6be42 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -1,12 +1,33 @@ class SectorsController < ApplicationController + CP_ALIGNMENTS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze + def index # get companies grouped by latest MQ assessments sector's levels @companies_data = Company - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .map { |k,v| ["Level #{k}", v.size]} + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .map { |k, v| ["Level #{k}", v.size] } + + @sectors_cp_data = CP_ALIGNMENTS.map do |cp_alignment| # CP alignments are series + {name: get_alignment_label(cp_alignment), data: get_companies_count_for_cp_alignment(cp_alignment)} + end + end + + def show; end + + def get_alignment_label(cp_alignment) + { + below_2: 'Below 2', + exact_2: '2 degrees', + paris: 'Paris', + not_aligned: 'Not aligned', + no_disclosure: 'No disclosure' + }[cp_alignment.to_sym] end - def show + def get_companies_count_for_cp_alignment(_cp_alignment) + Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| + [sector_name, rand(100)] + end end end diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index ebb0757c4..361c90d6c 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -1,5 +1,38 @@

Sectors dashboard

-

Management Quality: All sectors

+

Management Quality: All sectors

+

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

-<%= pie_chart @companies_data, donut: true, library: { tooltip: { enabled: false }, title: { text: '123 Companies' }, plotOptions: { pie: { dataLabels: { format: "{point.name}
{point.y} companies
{point.percentage:.1f}%" }} } } %> +<%= pie_chart( + @companies_data, + donut: true, + library: { + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' + } + } + } + } + ) %> + +

Market Cap

+

TODO: bubble chart for sectors / levels

+ +

Carbon Performance: All sectors

+

CP alignment with the Paris agreement benchmarks by sector and cluster (number and % of companies). Please note that this information is not available for all sectors.

+ +<%= column_chart( + @sectors_cp_data, + stacked: true, + width: '1200px', + library: { + plotOptions: { + column: { + stacking: 'percent' + } + }, + } + ) %> From 687c81eb811c173b646a7f1a001d7fa951b26a2b Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sat, 14 Sep 2019 08:32:31 +0200 Subject: [PATCH 03/56] add navigation to sectors and companies --- app/views/sectors/index.html.erb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 361c90d6c..146f4c094 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -1,5 +1,12 @@

Sectors dashboard

+Sectors: +<%= Sector.pluck(:id, :name).map {|s| link_to s.last, sectors_show_path(s.first)}.join(', ').html_safe %> +
+
+Companies: +<%= Company.pluck(:id, :name).take(20).map {|c| link_to c.last, companies_show_path(c.first)}.join(', ').html_safe %> +

Management Quality: All sectors

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

From be13b1aa55c82b0ab3d61fdc173bfe94797df53e Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sat, 14 Sep 2019 12:00:05 +0200 Subject: [PATCH 04/56] Sector details page with 1st chart --- app/controllers/sectors_controller.rb | 9 ++++++++- app/views/sectors/index.html.erb | 4 ++-- app/views/sectors/show.html.erb | 21 +++++++++++++++++++-- config/routes.rb | 7 ++++--- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 20bf6be42..dabf69eb4 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -13,7 +13,14 @@ def index end end - def show; end + def show + @sector = Sector.find(params[:id]) + + @companies_data = @sector.companies + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .map { |k, v| ["Level #{k}", v.size] } + end def get_alignment_label(cp_alignment) { diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 146f4c094..00ae4fe91 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -1,11 +1,11 @@

Sectors dashboard

Sectors: -<%= Sector.pluck(:id, :name).map {|s| link_to s.last, sectors_show_path(s.first)}.join(', ').html_safe %> +<%= Sector.pluck(:id, :name).map {|s| link_to s.last, sector_path(s.first)}.join(', ').html_safe %>

Companies: -<%= Company.pluck(:id, :name).take(20).map {|c| link_to c.last, companies_show_path(c.first)}.join(', ').html_safe %> +<%= Company.pluck(:id, :name).take(20).map {|c| link_to c.last, company_path(c.first)}.join(', ').html_safe %>

Management Quality: All sectors

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 7dc19b9df..2e7fbbee2 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -1,2 +1,19 @@ -

Sectors#show

-<%= pie_chart Company.group(:sector).count %> +

Sectors

+ +

Management Quality: <%= @sector.name %>

+

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

+ +<%= pie_chart( + @companies_data, + donut: true, + library: { + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' + } + } + } + } + ) %> diff --git a/config/routes.rb b/config/routes.rb index 8bc3ed499..6deb940bf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,8 @@ Rails.application.routes.draw do - get 'companies/show' - get 'sectors/index' - get 'sectors/show' + # TPI + resources :sectors, only: [:show, :index] + resources :companies, only: [:show] + devise_for :admin_users, ActiveAdmin::Devise.config ActiveAdmin.routes(self) From f01c28c5728e3023c43de80c1fc08c6ff2264495 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sat, 14 Sep 2019 12:06:30 +0200 Subject: [PATCH 05/56] remove empty files --- app/assets/stylesheets/companies.scss | 3 --- app/assets/stylesheets/sectors.scss | 3 --- app/decorators/sector_decorator.rb | 12 ------------ app/helpers/companies_helper.rb | 2 -- app/helpers/sectors_helper.rb | 2 -- 5 files changed, 22 deletions(-) delete mode 100644 app/assets/stylesheets/companies.scss delete mode 100644 app/assets/stylesheets/sectors.scss delete mode 100644 app/decorators/sector_decorator.rb delete mode 100644 app/helpers/companies_helper.rb delete mode 100644 app/helpers/sectors_helper.rb diff --git a/app/assets/stylesheets/companies.scss b/app/assets/stylesheets/companies.scss deleted file mode 100644 index 412c0f58b..000000000 --- a/app/assets/stylesheets/companies.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Companies controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sectors.scss b/app/assets/stylesheets/sectors.scss deleted file mode 100644 index a0fe8452e..000000000 --- a/app/assets/stylesheets/sectors.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Sectors controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/decorators/sector_decorator.rb b/app/decorators/sector_decorator.rb deleted file mode 100644 index 1faf03e2d..000000000 --- a/app/decorators/sector_decorator.rb +++ /dev/null @@ -1,12 +0,0 @@ -class SectorDecorator < Draper::Decorator - delegate_all - - # Define presentation-specific methods here. Helpers are accessed through - # `helpers` (aka `h`). You can override attributes, for example: - # - # def created_at - # helpers.content_tag :span, class: 'time' do - # object.created_at.strftime("%a %m/%d/%y") - # end - # end -end diff --git a/app/helpers/companies_helper.rb b/app/helpers/companies_helper.rb deleted file mode 100644 index 099b151b9..000000000 --- a/app/helpers/companies_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module CompaniesHelper -end diff --git a/app/helpers/sectors_helper.rb b/app/helpers/sectors_helper.rb deleted file mode 100644 index 42152977f..000000000 --- a/app/helpers/sectors_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module SectorsHelper -end From a4c125272cf1cd2bb46154eb3017e1de2f08de3a Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sat, 14 Sep 2019 12:47:09 +0200 Subject: [PATCH 06/56] sectors controller cleanup --- app/controllers/sectors_controller.rb | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index dabf69eb4..599f427c1 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -2,11 +2,7 @@ class SectorsController < ApplicationController CP_ALIGNMENTS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze def index - # get companies grouped by latest MQ assessments sector's levels - @companies_data = Company - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .map { |k, v| ["Level #{k}", v.size] } + @companies_data = get_companies_grouped_by_sector_levels(Company) @sectors_cp_data = CP_ALIGNMENTS.map do |cp_alignment| # CP alignments are series {name: get_alignment_label(cp_alignment), data: get_companies_count_for_cp_alignment(cp_alignment)} @@ -16,12 +12,23 @@ def index def show @sector = Sector.find(params[:id]) - @companies_data = @sector.companies + @companies_data = get_companies_grouped_by_sector_levels(@sector.companies) + end + + # get companies grouped by latest MQ assessments sector's levels + def get_companies_grouped_by_sector_levels(company_scope) + company_scope .includes(:mq_assessments) .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } .map { |k, v| ["Level #{k}", v.size] } end + def get_companies_count_for_cp_alignment(_cp_alignment) + Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| + [sector_name, rand(100)] + end + end + def get_alignment_label(cp_alignment) { below_2: 'Below 2', @@ -31,10 +38,4 @@ def get_alignment_label(cp_alignment) no_disclosure: 'No disclosure' }[cp_alignment.to_sym] end - - def get_companies_count_for_cp_alignment(_cp_alignment) - Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| - [sector_name, rand(100)] - end - end end From 760d91da2a7e3a991da61ace5152a7b0c670b1c6 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sun, 15 Sep 2019 08:25:24 +0200 Subject: [PATCH 07/56] sector levels column "chart" --- app/controllers/sectors_controller.rb | 2 +- app/views/sectors/index.html.erb | 2 +- app/views/sectors/show.html.erb | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 599f427c1..3e2e64cd3 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -20,7 +20,7 @@ def get_companies_grouped_by_sector_levels(company_scope) company_scope .includes(:mq_assessments) .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .map { |k, v| ["Level #{k}", v.size] } + .map { |sector_level, companies| ["Level #{sector_level}", companies] } end def get_companies_count_for_cp_alignment(_cp_alignment) diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 00ae4fe91..84187d922 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -11,7 +11,7 @@ Companies:

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

<%= pie_chart( - @companies_data, + @companies_data.map { |level_label, companies| [level_label, companies.size] }, donut: true, library: { tooltip: { enabled: false }, diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 2e7fbbee2..3c00dd900 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -1,10 +1,11 @@ -

Sectors

+

Sector Details

+<%= link_to 'back to all Sectors', sectors_path %>

Management Quality: <%= @sector.name %>

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

<%= pie_chart( - @companies_data, + @companies_data.map { |level_label, companies| [level_label, companies.size] }, donut: true, library: { tooltip: { enabled: false }, @@ -17,3 +18,24 @@ } } ) %> + +


+ +
+<%- @companies_data.sort_by { |level, _companies| level }.each do |level, companies| -%> +
+
+ <%= level %> +
+
+
    + <%- companies.each do |company| %> +
  • <%= company.name %>
  • + <%- end %> +
+
+
+<%- end %> +
+ +

Carbon Performance: <%= @sector.name %>

From 0f5a94c87e9ff736b44fae8153eec039ae93a97b Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Sun, 15 Sep 2019 08:40:28 +0200 Subject: [PATCH 08/56] add CP perf. per Setor chart (random data) --- app/views/sectors/index.html.erb | 2 +- app/views/sectors/show.html.erb | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 84187d922..77d041e0c 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -28,7 +28,7 @@ Companies:

Market Cap

TODO: bubble chart for sectors / levels

-

Carbon Performance: All sectors

+

Carbon Performance: All sectors (random data)

CP alignment with the Paris agreement benchmarks by sector and cluster (number and % of companies). Please note that this information is not available for all sectors.

<%= column_chart( diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 3c00dd900..008ce53cc 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -38,4 +38,13 @@ <%- end %> -

Carbon Performance: <%= @sector.name %>

+

Carbon Performance: <%= @sector.name %> (random data)

+<%= line_chart( + @companies_data.map {|_level, companies| companies }.flatten.take(10).map do |company| + { + name: company.name, + data: [[2014, rand(200)], [2015, rand(160)], [2016, rand(120)], [2017, rand(90)]] + } + end, + width: '1000' + )%> From ba1ce9f0e1328d35a176368f9345c6f353de1e32 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 16 Sep 2019 13:50:02 +0200 Subject: [PATCH 09/56] use real data on CP perf. chart for sector; add tpi stylesheets --- app/assets/stylesheets/tpi.scss | 6 ++++ app/controllers/sectors_controller.rb | 24 ++++++++------- app/views/layouts/application.html.erb | 3 +- app/views/sectors/index.html.erb | 20 ++++++++----- app/views/sectors/show.html.erb | 41 +++++++++++++++----------- config/initializers/assets.rb | 2 +- 6 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 app/assets/stylesheets/tpi.scss diff --git a/app/assets/stylesheets/tpi.scss b/app/assets/stylesheets/tpi.scss new file mode 100644 index 000000000..11fe46c21 --- /dev/null +++ b/app/assets/stylesheets/tpi.scss @@ -0,0 +1,6 @@ + +.chart { + display: flex; + flex-flow: row wrap; + justify-content: center; +} diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 3e2e64cd3..1c3d7627e 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -2,31 +2,27 @@ class SectorsController < ApplicationController CP_ALIGNMENTS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze def index - @companies_data = get_companies_grouped_by_sector_levels(Company) + @companies_by_levels = get_companies_grouped_by_sector_levels(Company) @sectors_cp_data = CP_ALIGNMENTS.map do |cp_alignment| # CP alignments are series - {name: get_alignment_label(cp_alignment), data: get_companies_count_for_cp_alignment(cp_alignment)} + { + name: get_alignment_label(cp_alignment), + data: get_companies_count_for_cp_alignment(cp_alignment) + } end end def show @sector = Sector.find(params[:id]) - @companies_data = get_companies_grouped_by_sector_levels(@sector.companies) + @companies_by_levels = get_companies_grouped_by_sector_levels(@sector.companies) end - # get companies grouped by latest MQ assessments sector's levels + # get companies grouped by their latest MQ assessments sector's levels def get_companies_grouped_by_sector_levels(company_scope) company_scope .includes(:mq_assessments) .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .map { |sector_level, companies| ["Level #{sector_level}", companies] } - end - - def get_companies_count_for_cp_alignment(_cp_alignment) - Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| - [sector_name, rand(100)] - end end def get_alignment_label(cp_alignment) @@ -38,4 +34,10 @@ def get_alignment_label(cp_alignment) no_disclosure: 'No disclosure' }[cp_alignment.to_sym] end + + def get_companies_count_for_cp_alignment(_cp_alignment) + Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| + [sector_name, rand(100)] + end + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 63064f505..3b16012e0 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,11 +1,12 @@ - LawsAndPathways + Laws & Pathways <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + <%= stylesheet_link_tag 'tpi' %> diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 77d041e0c..4b4f0a46b 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -10,20 +10,22 @@ Companies:

Management Quality: All sectors

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

+
<%= pie_chart( - @companies_data.map { |level_label, companies| [level_label, companies.size] }, + @companies_by_levels.map { |level, companies| ["Level #{level}", companies.size] }, donut: true, library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' - } + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' } } } - ) %> + } + ) %> +

Market Cap

TODO: bubble chart for sectors / levels

@@ -31,6 +33,7 @@ Companies:

Carbon Performance: All sectors (random data)

CP alignment with the Paris agreement benchmarks by sector and cluster (number and % of companies). Please note that this information is not available for all sectors.

+
<%= column_chart( @sectors_cp_data, stacked: true, @@ -43,3 +46,4 @@ Companies: }, } ) %> +
diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 008ce53cc..6f9b0d008 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -4,25 +4,27 @@

Management Quality: <%= @sector.name %>

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

+
<%= pie_chart( - @companies_data.map { |level_label, companies| [level_label, companies.size] }, + @companies_by_levels.map { |level, companies| ["Level #{level}", companies.size] }, donut: true, library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' - } + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' } } } - ) %> + } + ) %> +



-<%- @companies_data.sort_by { |level, _companies| level }.each do |level, companies| -%> +<%- @companies_by_levels.sort_by { |level, _companies| level }.each do |level, companies| -%>
<%= level %> @@ -38,13 +40,16 @@ <%- end %>
-

Carbon Performance: <%= @sector.name %> (random data)

+

Carbon Performance: <%= @sector.name %>

+
<%= line_chart( - @companies_data.map {|_level, companies| companies }.flatten.take(10).map do |company| - { - name: company.name, - data: [[2014, rand(200)], [2015, rand(160)], [2016, rand(120)], [2017, rand(90)]] - } - end, - width: '1000' - )%> + @companies_by_levels.map {|_level, companies| companies }.flatten.map do |company| + { + name: company.name, + data: company.cp_assessments.last&.emissions + } + end, + width: '1200px', + height: '800px' + )%> +
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e80c..649985239 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -11,4 +11,4 @@ # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w( tpi.css ) From 6cf35f71d99fbe0253cf8679decf06cfaf655ce7 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 16 Sep 2019 15:50:01 +0200 Subject: [PATCH 10/56] sector names --- app/controllers/sectors_controller.rb | 1 + app/views/sectors/index.html.erb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 1c3d7627e..724cc4524 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -4,6 +4,7 @@ class SectorsController < ApplicationController def index @companies_by_levels = get_companies_grouped_by_sector_levels(Company) + @sectors_names = Sector.pluck(:id, :name).sort_by { |_id, name| name } @sectors_cp_data = CP_ALIGNMENTS.map do |cp_alignment| # CP alignments are series { name: get_alignment_label(cp_alignment), diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 4b4f0a46b..223eea63b 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -1,7 +1,7 @@

Sectors dashboard

Sectors: -<%= Sector.pluck(:id, :name).map {|s| link_to s.last, sector_path(s.first)}.join(', ').html_safe %> +<%= @sectors_names.map {|s| link_to s.last, sector_path(s.first)}.join(', ').html_safe %>

Companies: From 43ad9e62fec5a7dd93dda71843ccc58aa1110c1f Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 12:03:57 +0200 Subject: [PATCH 11/56] clean up sectors controller and views for charts --- app/controllers/sectors_controller.rb | 62 +++++++++++++++++++++------ app/views/sectors/index.html.erb | 8 ++-- app/views/sectors/show.html.erb | 12 +++--- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 724cc4524..cf4ef01b6 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -1,31 +1,58 @@ class SectorsController < ApplicationController - CP_ALIGNMENTS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze + CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze def index - @companies_by_levels = get_companies_grouped_by_sector_levels(Company) + @companies_by_levels = companies_grouped_by_sector_levels(Company) + + @sectors_cp_data = sectors_scenarios_alignments @sectors_names = Sector.pluck(:id, :name).sort_by { |_id, name| name } - @sectors_cp_data = CP_ALIGNMENTS.map do |cp_alignment| # CP alignments are series - { - name: get_alignment_label(cp_alignment), - data: get_companies_count_for_cp_alignment(cp_alignment) - } - end + @companies_names = Company.limit(20).pluck(:id, :name) end def show @sector = Sector.find(params[:id]) - @companies_by_levels = get_companies_grouped_by_sector_levels(@sector.companies) + @companies_by_levels = companies_grouped_by_sector_levels(@sector.companies) end - # get companies grouped by their latest MQ assessments sector's levels - def get_companies_grouped_by_sector_levels(company_scope) + # Returns info of how many companies from each sector are in given CP scenario group + # + # [ + # { + # name: 'Below 2', + # data: [ ['Coal Mining', 52], ['Steel', 73] ] + # }, + # { + # name: 'Paris', + # data: [ ['Coal Mining', 65], ['Steel', 26] ] + # } + # ] + def sectors_scenarios_alignments + CP_SCENARIOS.map do |cp_alignment| + { + name: get_alignment_label(cp_alignment), + data: companies_count_per_sector_cp_scenarios(cp_alignment) + } + end + end + + # Returns companies grouped by their latest MQ assessments sector's levels + # + # [ + # ['1', ['Air China', 'China Southern', 'Korean Air', 'Singapore Airlines']], + # ['3', ['Alaska Air', 'IAG', 'Japan Airlines', 'Jetblue', 'LATAM', 'Qantas']] + # ] + def companies_grouped_by_sector_levels(company_scope) company_scope .includes(:mq_assessments) .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .sort_by { |level, _companies| level } + .map { |level, companies| [level, companies_emissions(companies)] } end + private + def get_alignment_label(cp_alignment) { below_2: 'Below 2', @@ -36,9 +63,18 @@ def get_alignment_label(cp_alignment) }[cp_alignment.to_sym] end - def get_companies_count_for_cp_alignment(_cp_alignment) - Sector.take(10).pluck(:id, :name).map do |_sector_id, sector_name| + def companies_count_per_sector_cp_scenarios(_cp_alignment) + Sector.pluck(:id, :name).map do |_sector_id, sector_name| [sector_name, rand(100)] end end + + def companies_emissions(companies) + companies.map do |company| + { + name: company.name, + emissions: company.cp_assessments.last&.emissions + } + end + end end diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 223eea63b..2d16c1b0f 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -5,21 +5,23 @@ Sectors:

Companies: -<%= Company.pluck(:id, :name).take(20).map {|c| link_to c.last, company_path(c.first)}.join(', ').html_safe %> +<%= @companies_names.map {|c| link_to c.last, company_path(c.first)}.join(', ').html_safe %> +
+

Management Quality: All sectors

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

<%= pie_chart( - @companies_by_levels.map { |level, companies| ["Level #{level}", companies.size] }, + @companies_by_levels.map { |level, companies| [level, companies.size] }, donut: true, library: { tooltip: { enabled: false }, plotOptions: { pie: { dataLabels: { - format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' + format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' } } } diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 6f9b0d008..df5ce2809 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -6,14 +6,14 @@
<%= pie_chart( - @companies_by_levels.map { |level, companies| ["Level #{level}", companies.size] }, + @companies_by_levels.map { |level, companies| [level, companies.size] }, donut: true, library: { tooltip: { enabled: false }, plotOptions: { pie: { dataLabels: { - format: '{point.name}
{point.y} companies
{point.percentage:.1f}%' + format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' } } } @@ -24,7 +24,7 @@


-<%- @companies_by_levels.sort_by { |level, _companies| level }.each do |level, companies| -%> +<%- @companies_by_levels.each do |level, companies| -%>
<%= level %> @@ -32,7 +32,7 @@
    <%- companies.each do |company| %> -
  • <%= company.name %>
  • +
  • <%= company[:name] %>
  • <%- end %>
@@ -45,8 +45,8 @@ <%= line_chart( @companies_by_levels.map {|_level, companies| companies }.flatten.map do |company| { - name: company.name, - data: company.cp_assessments.last&.emissions + name: company[:name], + data: company[:emissions] } end, width: '1200px', From 276cd4be5b2cb05e96f3ae319c80f22c4b6cde36 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 17:12:30 +0200 Subject: [PATCH 12/56] extract API services --- app/controllers/sectors_controller.rb | 48 ++----------------- .../companies_count_grouped_by_scenario.rb | 34 +++++++++++++ .../companies_names_grouped_by_level.rb | 28 +++++++++++ 3 files changed, 67 insertions(+), 43 deletions(-) create mode 100644 app/services/api/sectors/companies_count_grouped_by_scenario.rb create mode 100644 app/services/api/sectors/companies_names_grouped_by_level.rb diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index cf4ef01b6..08300a214 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -1,12 +1,10 @@ class SectorsController < ApplicationController - CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze - def index @companies_by_levels = companies_grouped_by_sector_levels(Company) @sectors_cp_data = sectors_scenarios_alignments - @sectors_names = Sector.pluck(:id, :name).sort_by { |_id, name| name } + @sectors_names = Sector.order(:name).pluck(:id, :name) @companies_names = Company.limit(20).pluck(:id, :name) end @@ -29,52 +27,16 @@ def show # } # ] def sectors_scenarios_alignments - CP_SCENARIOS.map do |cp_alignment| - { - name: get_alignment_label(cp_alignment), - data: companies_count_per_sector_cp_scenarios(cp_alignment) - } - end + ::Api::Sectors::CompaniesCountGroupedByScenario.new.get end # Returns companies grouped by their latest MQ assessments sector's levels # # [ - # ['1', ['Air China', 'China Southern', 'Korean Air', 'Singapore Airlines']], - # ['3', ['Alaska Air', 'IAG', 'Japan Airlines', 'Jetblue', 'LATAM', 'Qantas']] + # ['1', ['Air China', 'China Southern', 'Singapore Airlines']], + # ['3', ['Alaska Air', 'IAG', 'Japan Airlines']] # ] def companies_grouped_by_sector_levels(company_scope) - company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .sort_by { |level, _companies| level } - .map { |level, companies| [level, companies_emissions(companies)] } - end - - private - - def get_alignment_label(cp_alignment) - { - below_2: 'Below 2', - exact_2: '2 degrees', - paris: 'Paris', - not_aligned: 'Not aligned', - no_disclosure: 'No disclosure' - }[cp_alignment.to_sym] - end - - def companies_count_per_sector_cp_scenarios(_cp_alignment) - Sector.pluck(:id, :name).map do |_sector_id, sector_name| - [sector_name, rand(100)] - end - end - - def companies_emissions(companies) - companies.map do |company| - { - name: company.name, - emissions: company.cp_assessments.last&.emissions - } - end + ::Api::Sectors::CompaniesNamesGroupedByLevel.new(company_scope).get end end diff --git a/app/services/api/sectors/companies_count_grouped_by_scenario.rb b/app/services/api/sectors/companies_count_grouped_by_scenario.rb new file mode 100644 index 000000000..1df3d4bcb --- /dev/null +++ b/app/services/api/sectors/companies_count_grouped_by_scenario.rb @@ -0,0 +1,34 @@ +module Api + module Sectors + class CompaniesCountGroupedByScenario + CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze + + def get + CP_SCENARIOS.map do |cp_alignment| + { + name: get_alignment_label(cp_alignment), + data: companies_count_per_sector_cp_scenarios(cp_alignment) + } + end + end + + private + + def get_alignment_label(cp_alignment) + { + below_2: 'Below 2', + exact_2: '2 degrees', + paris: 'Paris', + not_aligned: 'Not aligned', + no_disclosure: 'No disclosure' + }[cp_alignment.to_sym] + end + + def companies_count_per_sector_cp_scenarios(_cp_alignment) + Sector.pluck(:id, :name).map do |_sector_id, sector_name| + [sector_name, rand(100)] + end + end + end + end +end diff --git a/app/services/api/sectors/companies_names_grouped_by_level.rb b/app/services/api/sectors/companies_names_grouped_by_level.rb new file mode 100644 index 000000000..0f9d79406 --- /dev/null +++ b/app/services/api/sectors/companies_names_grouped_by_level.rb @@ -0,0 +1,28 @@ +module Api + module Sectors + class CompaniesNamesGroupedByLevel + def initialize(company_scope) + @company_scope = company_scope + end + + def get + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .sort_by { |level, _companies| level } + .map { |level, companies| [level, companies_emissions(companies)] } + end + + private + + def companies_emissions(companies) + companies.map do |company| + { + name: company.name, + emissions: company.cp_assessments.last&.emissions + } + end + end + end + end +end From 7fcf8624c2df630a187defff8ffdbff079cae061 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 20:34:20 +0200 Subject: [PATCH 13/56] dedicated endpoints for charts --- app/assets/stylesheets/tpi.scss | 5 ++ app/controllers/sectors_controller.rb | 50 ++++++++++++++++--- .../companies_names_grouped_by_level.rb | 2 +- app/views/sectors/index.html.erb | 16 +++--- app/views/sectors/show.html.erb | 23 ++++----- config/routes.rb | 11 +++- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/tpi.scss b/app/assets/stylesheets/tpi.scss index 11fe46c21..c109a1f7c 100644 --- a/app/assets/stylesheets/tpi.scss +++ b/app/assets/stylesheets/tpi.scss @@ -1,3 +1,8 @@ +.container { + display: flex; + flex-direction: row; + justify-content: center; +} .chart { display: flex; diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 08300a214..553f296dc 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -1,9 +1,5 @@ class SectorsController < ApplicationController def index - @companies_by_levels = companies_grouped_by_sector_levels(Company) - - @sectors_cp_data = sectors_scenarios_alignments - @sectors_names = Sector.order(:name).pluck(:id, :name) @companies_names = Company.limit(20).pluck(:id, :name) end @@ -11,7 +7,28 @@ def index def show @sector = Sector.find(params[:id]) - @companies_by_levels = companies_grouped_by_sector_levels(@sector.companies) + @companies_by_levels = companies_grouped_by_sector_levels(companies_scope(params)) + end + + # chart data endpoints + + def companies_levels + data = companies_grouped_by_sector_levels(companies_scope(params)) + .map { |level, companies| [level, companies.size] } + + render json: data.chart_json + end + + def companies_emissions + data = companies_grouped_by_sector_levels(companies_scope(params)) + .map { |_level, companies| companies } + .flatten.map { |company| {name: company[:name], data: company[:emissions]} } + + render json: data.chart_json + end + + def scenarios + render json: sectors_scenarios_alignments.chart_json end # Returns info of how many companies from each sector are in given CP scenario group @@ -33,10 +50,29 @@ def sectors_scenarios_alignments # Returns companies grouped by their latest MQ assessments sector's levels # # [ - # ['1', ['Air China', 'China Southern', 'Singapore Airlines']], - # ['3', ['Alaska Air', 'IAG', 'Japan Airlines']] + # ['1', + # [ + # { name: 'Air China', emissions: { 2013: 153, 2014: 142 } }, + # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 } } + # ] + # ], + # ['3', + # [ + # { name: 'Alaska Air', emissions: { } }, + # { name: 'IAG', emissions: { } } + # { name: 'Japan Airlines', emissions: { } } + # ] + # ] # ] def companies_grouped_by_sector_levels(company_scope) ::Api::Sectors::CompaniesNamesGroupedByLevel.new(company_scope).get end + + def companies_scope(params) + if params[:id] + Sector.find(params[:id]).companies + else + Company + end + end end diff --git a/app/services/api/sectors/companies_names_grouped_by_level.rb b/app/services/api/sectors/companies_names_grouped_by_level.rb index 0f9d79406..4c1ac2a33 100644 --- a/app/services/api/sectors/companies_names_grouped_by_level.rb +++ b/app/services/api/sectors/companies_names_grouped_by_level.rb @@ -19,7 +19,7 @@ def companies_emissions(companies) companies.map do |company| { name: company.name, - emissions: company.cp_assessments.last&.emissions + emissions: company.cp_assessments.last&.emissions || {} } end end diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 2d16c1b0f..883551f32 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -14,18 +14,18 @@ Companies:
<%= pie_chart( - @companies_by_levels.map { |level, companies| [level, companies.size] }, + companies_levels_sectors_path, donut: true, library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' + } } } } - } ) %>
@@ -37,7 +37,7 @@ Companies:
<%= column_chart( - @sectors_cp_data, + scenarios_sectors_path, stacked: true, width: '1200px', library: { diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index df5ce2809..43869cc99 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -6,24 +6,24 @@
<%= pie_chart( - @companies_by_levels.map { |level, companies| [level, companies.size] }, + companies_levels_sector_path(@sector.id), donut: true, library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' + tooltip: { enabled: false }, + plotOptions: { + pie: { + dataLabels: { + format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' + } } } } - } ) %>



-
+
<%- @companies_by_levels.each do |level, companies| -%>
@@ -43,12 +43,7 @@

Carbon Performance: <%= @sector.name %>

<%= line_chart( - @companies_by_levels.map {|_level, companies| companies }.flatten.map do |company| - { - name: company[:name], - data: company[:emissions] - } - end, + companies_emissions_sector_path(@sector.id), width: '1200px', height: '800px' )%> diff --git a/config/routes.rb b/config/routes.rb index 6deb940bf..2c979ed81 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,15 @@ Rails.application.routes.draw do # TPI - resources :sectors, only: [:show, :index] + resources :sectors, only: [:show, :index] do + collection do + get :companies_levels + get :scenarios + end + member do + get :companies_levels + get :companies_emissions + end + end resources :companies, only: [:show] devise_for :admin_users, ActiveAdmin::Devise.config From 09339d4e6b9bc4cf72e3cbe428e964267b8404c7 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 20:46:22 +0200 Subject: [PATCH 14/56] comments --- app/controllers/sectors_controller.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 553f296dc..107a82b32 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -12,6 +12,15 @@ def show # chart data endpoints + # Returns data: + # [ + # ['0', 13], + # ['1', 63], + # ['2', 61], + # ['3', 71], + # ['4', 63], + # ['4STAR', 6] + # ] def companies_levels data = companies_grouped_by_sector_levels(companies_scope(params)) .map { |level, companies| [level, companies.size] } @@ -19,6 +28,11 @@ def companies_levels render json: data.chart_json end + # [ + # { name: 'WizzAir', data: {} }, + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] def companies_emissions data = companies_grouped_by_sector_levels(companies_scope(params)) .map { |_level, companies| companies } From c2bb2fe72c8e2bdb920a22c263e77eb0bb922d7b Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 20:59:49 +0200 Subject: [PATCH 15/56] better comments --- app/controllers/sectors_controller.rb | 57 +++++++------------ .../companies_count_grouped_by_scenario.rb | 12 ++++ .../companies_names_grouped_by_level.rb | 17 ++++++ 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 107a82b32..a02a08b07 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -10,9 +10,14 @@ def show @companies_by_levels = companies_grouped_by_sector_levels(companies_scope(params)) end - # chart data endpoints + # Chart data endpoints - # Returns data: + # Returns array of pairs: + # [latest MQ assessment Sector's level, number of Companies on given level] + # + # If params[:id] is given results are scoped to given Sector. + # + # @example: # [ # ['0', 13], # ['1', 63], @@ -21,6 +26,7 @@ def show # ['4', 63], # ['4STAR', 6] # ] + # def companies_levels data = companies_grouped_by_sector_levels(companies_scope(params)) .map { |level, companies| [level, companies.size] } @@ -28,11 +34,19 @@ def companies_levels render json: data.chart_json end - # [ - # { name: 'WizzAir', data: {} }, - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } - # ] + # Returns array of objects with props: + # - name - Company name + # - data - object representing emissions history ({ year: emission-value }) + # + # If params[:id] is given results are scoped to given Sector. + # + # @example + # [ + # { name: 'WizzAir', data: {} }, + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] + # def companies_emissions data = companies_grouped_by_sector_levels(companies_scope(params)) .map { |_level, companies| companies } @@ -45,39 +59,10 @@ def scenarios render json: sectors_scenarios_alignments.chart_json end - # Returns info of how many companies from each sector are in given CP scenario group - # - # [ - # { - # name: 'Below 2', - # data: [ ['Coal Mining', 52], ['Steel', 73] ] - # }, - # { - # name: 'Paris', - # data: [ ['Coal Mining', 65], ['Steel', 26] ] - # } - # ] def sectors_scenarios_alignments ::Api::Sectors::CompaniesCountGroupedByScenario.new.get end - # Returns companies grouped by their latest MQ assessments sector's levels - # - # [ - # ['1', - # [ - # { name: 'Air China', emissions: { 2013: 153, 2014: 142 } }, - # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 } } - # ] - # ], - # ['3', - # [ - # { name: 'Alaska Air', emissions: { } }, - # { name: 'IAG', emissions: { } } - # { name: 'Japan Airlines', emissions: { } } - # ] - # ] - # ] def companies_grouped_by_sector_levels(company_scope) ::Api::Sectors::CompaniesNamesGroupedByLevel.new(company_scope).get end diff --git a/app/services/api/sectors/companies_count_grouped_by_scenario.rb b/app/services/api/sectors/companies_count_grouped_by_scenario.rb index 1df3d4bcb..2ef33311d 100644 --- a/app/services/api/sectors/companies_count_grouped_by_scenario.rb +++ b/app/services/api/sectors/companies_count_grouped_by_scenario.rb @@ -3,6 +3,18 @@ module Sectors class CompaniesCountGroupedByScenario CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze + # Returns info of how many Companies from each Sector are in given CP scenario group + # + # [ + # { + # name: 'Below 2', + # data: [ ['Coal Mining', 52], ['Steel', 73] ] + # }, + # { + # name: 'Paris', + # data: [ ['Coal Mining', 65], ['Steel', 26] ] + # } + # ] def get CP_SCENARIOS.map do |cp_alignment| { diff --git a/app/services/api/sectors/companies_names_grouped_by_level.rb b/app/services/api/sectors/companies_names_grouped_by_level.rb index 4c1ac2a33..8df2ab8c4 100644 --- a/app/services/api/sectors/companies_names_grouped_by_level.rb +++ b/app/services/api/sectors/companies_names_grouped_by_level.rb @@ -5,6 +5,23 @@ def initialize(company_scope) @company_scope = company_scope end + # Returns Companies grouped by their latest MQ assessments Sector's levels + # + # [ + # ['1', + # [ + # { name: 'Air China', emissions: { 2013: 153, 2014: 142 } }, + # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 } } + # ] + # ], + # ['3', + # [ + # { name: 'Alaska Air', emissions: { } }, + # { name: 'IAG', emissions: { } } + # { name: 'Japan Airlines', emissions: { } } + # ] + # ] + # ] def get @company_scope .includes(:mq_assessments) From 6a69049d89797f00783ee1d70b9ea9baf8c687d0 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 17 Sep 2019 21:02:47 +0200 Subject: [PATCH 16/56] remove empty files --- app/assets/javascripts/companies.js | 2 -- app/assets/javascripts/sectors.js | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 app/assets/javascripts/companies.js delete mode 100644 app/assets/javascripts/sectors.js diff --git a/app/assets/javascripts/companies.js b/app/assets/javascripts/companies.js deleted file mode 100644 index dee720fac..000000000 --- a/app/assets/javascripts/companies.js +++ /dev/null @@ -1,2 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/sectors.js b/app/assets/javascripts/sectors.js deleted file mode 100644 index dee720fac..000000000 --- a/app/assets/javascripts/sectors.js +++ /dev/null @@ -1,2 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. From 10d9d7a137dc48361b2760bf79a213550106ab71 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Wed, 18 Sep 2019 09:46:40 +0200 Subject: [PATCH 17/56] clean up --- app/controllers/sectors_controller.rb | 13 ++------- ...level.rb => companies_grouped_by_level.rb} | 28 ++++++++++++++++++- 2 files changed, 30 insertions(+), 11 deletions(-) rename app/services/api/sectors/{companies_names_grouped_by_level.rb => companies_grouped_by_level.rb} (61%) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index a02a08b07..92f27f118 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -7,7 +7,7 @@ def index def show @sector = Sector.find(params[:id]) - @companies_by_levels = companies_grouped_by_sector_levels(companies_scope(params)) + @companies_by_levels = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).get end # Chart data endpoints @@ -28,8 +28,7 @@ def show # ] # def companies_levels - data = companies_grouped_by_sector_levels(companies_scope(params)) - .map { |level, companies| [level, companies.size] } + data = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).count render json: data.chart_json end @@ -48,9 +47,7 @@ def companies_levels # ] # def companies_emissions - data = companies_grouped_by_sector_levels(companies_scope(params)) - .map { |_level, companies| companies } - .flatten.map { |company| {name: company[:name], data: company[:emissions]} } + data = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).emissions render json: data.chart_json end @@ -63,10 +60,6 @@ def sectors_scenarios_alignments ::Api::Sectors::CompaniesCountGroupedByScenario.new.get end - def companies_grouped_by_sector_levels(company_scope) - ::Api::Sectors::CompaniesNamesGroupedByLevel.new(company_scope).get - end - def companies_scope(params) if params[:id] Sector.find(params[:id]).companies diff --git a/app/services/api/sectors/companies_names_grouped_by_level.rb b/app/services/api/sectors/companies_grouped_by_level.rb similarity index 61% rename from app/services/api/sectors/companies_names_grouped_by_level.rb rename to app/services/api/sectors/companies_grouped_by_level.rb index 8df2ab8c4..57a0ec3cf 100644 --- a/app/services/api/sectors/companies_names_grouped_by_level.rb +++ b/app/services/api/sectors/companies_grouped_by_level.rb @@ -1,6 +1,7 @@ module Api module Sectors - class CompaniesNamesGroupedByLevel + # rename to CompaniesGroupedByLevel + class CompaniesGroupedByLevel def initialize(company_scope) @company_scope = company_scope end @@ -30,6 +31,31 @@ def get .map { |level, companies| [level, companies_emissions(companies)] } end + # [ + # ['0', 13], + # ['1', 63], + # ['2', 61], + # ['3', 71], + # ['4', 63], + # ['4STAR', 6] + # ] + # + def count + get.map { |level, companies| [level, companies.size] } + end + + # [ + # { name: 'WizzAir', data: {} }, + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] + # + def emissions + get + .map { |_level, companies| companies } + .flatten.map { |company| {name: company[:name], data: company[:emissions]} } + end + private def companies_emissions(companies) From 000a50c79d4c180fcf33b5092f3beac6e474320d Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Wed, 18 Sep 2019 15:00:47 +0200 Subject: [PATCH 18/56] draft of company show page --- app/admin/cp_assessments.rb | 1 + app/assets/stylesheets/tpi.scss | 8 +++ app/controllers/companies_controller.rb | 23 ++++++- app/controllers/sectors_controller.rb | 2 +- .../api/sectors/companies_grouped_by_level.rb | 11 ++- app/views/companies/show.html.erb | 67 ++++++++++++++++++- app/views/sectors/show.html.erb | 4 +- 7 files changed, 104 insertions(+), 12 deletions(-) diff --git a/app/admin/cp_assessments.rb b/app/admin/cp_assessments.rb index 87dbc2f7b..3b9490c4f 100644 --- a/app/admin/cp_assessments.rb +++ b/app/admin/cp_assessments.rb @@ -34,6 +34,7 @@ index do column :title, &:title_link + column :company column :assessment_date column :publication_date actions diff --git a/app/assets/stylesheets/tpi.scss b/app/assets/stylesheets/tpi.scss index c109a1f7c..5ef30c552 100644 --- a/app/assets/stylesheets/tpi.scss +++ b/app/assets/stylesheets/tpi.scss @@ -2,6 +2,14 @@ display: flex; flex-direction: row; justify-content: center; + gap: 5px; +} + +container-columns { + display: flex; + flex-direction: column; + justify-content: center; + gap: 5px; } .chart { diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 1039c605d..9e51fa8d9 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -1,3 +1,24 @@ class CompaniesController < ApplicationController - def show; end + def show + @company = Company.find(params[:id]) + + @company_info = { + country: @company.geography.name, + sector: @company.sector.name, + market_cap: 'Large', + isin: @company.isin, + sedol: 60, + ca100: @company.ca100 ? 'Yes' : 'No' + } + + @company_assessments = @company.latest_assessment.questions.group_by { |q| q['level'] } + + @levels_descriptions = { + '0' => 'Unaware of Climate Change as a Business Issue', + '1' => 'Acknowledging Climate Change as a Business Issue', + '2' => 'Building Capacity', + '3' => 'Integrating into Operational Decision Making', + '4' => 'Strategic Assessment' + } + end end diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 92f27f118..6e2b144e9 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -1,7 +1,7 @@ class SectorsController < ApplicationController def index @sectors_names = Sector.order(:name).pluck(:id, :name) - @companies_names = Company.limit(20).pluck(:id, :name) + @companies_names = Company.limit(50).pluck(:id, :name) end def show diff --git a/app/services/api/sectors/companies_grouped_by_level.rb b/app/services/api/sectors/companies_grouped_by_level.rb index 57a0ec3cf..a5f26793b 100644 --- a/app/services/api/sectors/companies_grouped_by_level.rb +++ b/app/services/api/sectors/companies_grouped_by_level.rb @@ -1,6 +1,5 @@ module Api module Sectors - # rename to CompaniesGroupedByLevel class CompaniesGroupedByLevel def initialize(company_scope) @company_scope = company_scope @@ -11,15 +10,14 @@ def initialize(company_scope) # [ # ['1', # [ - # { name: 'Air China', emissions: { 2013: 153, 2014: 142 } }, - # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 } } + # { name: 'Air China', emissions: { 2013: 153, 2014: 142 }, status: '' }, + # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 }, status: '' } # ] # ], # ['3', # [ - # { name: 'Alaska Air', emissions: { } }, - # { name: 'IAG', emissions: { } } - # { name: 'Japan Airlines', emissions: { } } + # { name: 'Alaska Air', emissions: { }, status: '' }, + # { name: 'IAG', emissions: { }, status: '' } # ] # ] # ] @@ -62,6 +60,7 @@ def companies_emissions(companies) companies.map do |company| { name: company.name, + status: company.mq_status, emissions: company.cp_assessments.last&.emissions || {} } end diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index 65b8dd114..758a2b7ba 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -1,2 +1,65 @@ -

Companies#show

-

Find me in app/views/companies/show.html.erb

+

Company Details

+<%= link_to 'back to all Sectors', sectors_path %> + +
+
+

Management Quality

+
Number of assessments: <%= @company.mq_assessments.size %>
+
+ <%= @company.mq_level %> + <%= @company.mq_status %> +
+
+ +
+

Carbon Performance

+
Number of assessments: <%= @company.cp_assessments.size %>
+
+ Below 2 degrees +
+
+
+ +

+ +
+ <%= @company_info.inspect %> +
+ +
+ +

Management Quality: <%= @company.name %>

+

Assessment of <%= @company.name %> according to the management of their greenhouse gas emissions and of risks and opportunities related to the low-carbon transition.

+ +
+
+ <%= @company.mq_level %> + <%= @company.mq_status %> +
TODO: chart ..
+
+
+
TODO: chart with assessments ..
+
+
+ +

+ +
+<%- @company_assessments.each do |level, assessments| %> +
+

Level <%= level %>: <%= @levels_descriptions[level.to_s] || 'no description yet' %>

+
+ <%- assessments.each do |a| %> +
+
(<%= a['answer'] %>)
+
<%= a['question'] %>
+
+ <%- end %> +
+
+<%- end %> +
+ + +

Carbon Performance <%= @company.name %>

+

Carbon Performance alignment of <%= @company.name %> with the Paris agreement benchmarks.

diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 43869cc99..100d5cc47 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -2,7 +2,7 @@ <%= link_to 'back to all Sectors', sectors_path %>

Management Quality: <%= @sector.name %>

-

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

+

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities related to the low-carbon transition.

<%= pie_chart( @@ -32,7 +32,7 @@
    <%- companies.each do |company| %> -
  • <%= company[:name] %>
  • +
  • <%= company[:name] %> [<%= company[:status] %>]
  • <%- end %>
From a3fa27ef3b6a1c3139eef9f07657c11c2a8a52b5 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Thu, 19 Sep 2019 08:45:17 +0200 Subject: [PATCH 19/56] split page sections in visible way --- app/views/companies/show.html.erb | 5 ++++- app/views/sectors/index.html.erb | 8 ++++++-- app/views/sectors/show.html.erb | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index 758a2b7ba..e6e211f95 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -1,6 +1,8 @@

Company Details

<%= link_to 'back to all Sectors', sectors_path %> +
+

Management Quality

@@ -47,7 +49,7 @@
<%- @company_assessments.each do |level, assessments| %>
-

Level <%= level %>: <%= @levels_descriptions[level.to_s] || 'no description yet' %>

+

Level <%= level %>: <%= @levels_descriptions[level.to_s] || 'Answers' %>

<%- assessments.each do |a| %>
@@ -60,6 +62,7 @@ <%- end %>
+

Carbon Performance <%= @company.name %>

Carbon Performance alignment of <%= @company.name %> with the Paris agreement benchmarks.

diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 883551f32..926822dfd 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -6,8 +6,8 @@ Sectors:
Companies: <%= @companies_names.map {|c| link_to c.last, company_path(c.first)}.join(', ').html_safe %> -
-
+ +


Management Quality: All sectors

Distribution of companies in All sectors according to the management of their greenhouse gas emissions and of risks and opportunities to the low-carbon transition.

@@ -29,9 +29,13 @@ Companies: ) %>
+
+

Market Cap

TODO: bubble chart for sectors / levels

+
+

Carbon Performance: All sectors (random data)

CP alignment with the Paris agreement benchmarks by sector and cluster (number and % of companies). Please note that this information is not available for all sectors.

diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 100d5cc47..3aa801659 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -1,6 +1,8 @@

Sector Details

<%= link_to 'back to all Sectors', sectors_path %> +
+

Management Quality: <%= @sector.name %>

Distribution of companies in the <%= @sector.name %> sector according to the management of their greenhouse gas emissions and of risks and opportunities related to the low-carbon transition.

@@ -40,6 +42,8 @@ <%- end %>
+
+

Carbon Performance: <%= @sector.name %>

<%= line_chart( From 772fbf299bd8621fa464292f10592575745e7ec6 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Thu, 19 Sep 2019 12:33:06 +0200 Subject: [PATCH 20/56] emissions chart for given company --- app/controllers/companies_controller.rb | 36 ++++++++++++++----------- app/views/companies/show.html.erb | 21 ++++++++++++--- app/views/sectors/index.html.erb | 3 +++ app/views/sectors/show.html.erb | 10 ++++++- config/routes.rb | 6 ++++- 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 9e51fa8d9..ad09e236b 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -2,23 +2,27 @@ class CompaniesController < ApplicationController def show @company = Company.find(params[:id]) - @company_info = { - country: @company.geography.name, - sector: @company.sector.name, - market_cap: 'Large', - isin: @company.isin, - sedol: 60, - ca100: @company.ca100 ? 'Yes' : 'No' - } + @company_details = ::Api::Companies::Details.new(@company).get + end + + # Returns array of 2 elements: + # - company emissions + # - company's sector emissions + # + # @example + # [ + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'Airlines sector mean', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] + # + def emissions + @company = Company.find(params[:id]) + + data = ::Api::Companies::Emissions.new(@company).get - @company_assessments = @company.latest_assessment.questions.group_by { |q| q['level'] } + # TODO: move to JS + @min_y_axis_value = data.pluck(:data).first.values.min - 20 - @levels_descriptions = { - '0' => 'Unaware of Climate Change as a Business Issue', - '1' => 'Acknowledging Climate Change as a Business Issue', - '2' => 'Building Capacity', - '3' => 'Integrating into Operational Decision Making', - '4' => 'Strategic Assessment' - } + render json: data.chart_json end end diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index e6e211f95..ee1c70091 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -25,7 +25,7 @@

- <%= @company_info.inspect %> + <%= @company_details.slice(:name, :country, :sector, :market_cap, :isin, :sedol) %>

@@ -47,9 +47,9 @@

-<%- @company_assessments.each do |level, assessments| %> +<%- @company_details[:latest_assessment].each do |level, assessments| %>
-

Level <%= level %>: <%= @levels_descriptions[level.to_s] || 'Answers' %>

+

Level <%= level %>: <%= @company_details[:levels_descriptions][level.to_s] || 'Answers' %>

<%- assessments.each do |a| %>
@@ -66,3 +66,18 @@

Carbon Performance <%= @company.name %>

Carbon Performance alignment of <%= @company.name %> with the Paris agreement benchmarks.

+
+<%= line_chart( + emissions_company_path(@company.id), + width: '800px', + height: '600px', + library: { + legend: { + verticalAlign: 'top', + }, + yAxis: { + min: @min_y_axis_value + } + } + )%> +
\ No newline at end of file diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 926822dfd..bff7a4a3d 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -45,6 +45,9 @@ Companies: stacked: true, width: '1200px', library: { + legend: { + verticalAlign: 'top', + }, plotOptions: { column: { stacking: 'percent' diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 3aa801659..9f9c7b061 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -49,6 +49,14 @@ <%= line_chart( companies_emissions_sector_path(@sector.id), width: '1200px', - height: '800px' + height: '800px', + library: { + legend: { + verticalAlign: 'top', + }, + yAxis: { + min: @min_y_axis_value + } + } )%>
diff --git a/config/routes.rb b/config/routes.rb index 2c979ed81..65671599c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,7 +10,11 @@ get :companies_emissions end end - resources :companies, only: [:show] + resources :companies, only: [:show] do + member do + get :emissions + end + end devise_for :admin_users, ActiveAdmin::Devise.config ActiveAdmin.routes(self) From ada749dba59d2fe40ba266cea50244922af26668 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Thu, 19 Sep 2019 18:04:06 +0200 Subject: [PATCH 21/56] company CP chart improvements --- app/controllers/companies_controller.rb | 4 +- app/services/api/charts/company.rb | 61 +++++++++++++++++++ app/services/api/companies/emissions.rb | 45 ++++++++++++++ .../companies_count_grouped_by_scenario.rb | 3 +- .../api/sectors/companies_grouped_by_level.rb | 24 +++++++- 5 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 app/services/api/charts/company.rb create mode 100644 app/services/api/companies/emissions.rb diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index ad09e236b..5f0987cb3 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -2,7 +2,7 @@ class CompaniesController < ApplicationController def show @company = Company.find(params[:id]) - @company_details = ::Api::Companies::Details.new(@company).get + @company_details = ::Api::Charts::Company.new.details_data(@company) end # Returns array of 2 elements: @@ -18,7 +18,7 @@ def show def emissions @company = Company.find(params[:id]) - data = ::Api::Companies::Emissions.new(@company).get + data = ::Api::Charts::Company.new.emissions_data(@company) # TODO: move to JS @min_y_axis_value = data.pluck(:data).first.values.min - 20 diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb new file mode 100644 index 000000000..1f44a7407 --- /dev/null +++ b/app/services/api/charts/company.rb @@ -0,0 +1,61 @@ +module Api + module Charts + class Company + SECTORS_LEVELS_DESC = { + '0' => 'Unaware of Climate Change as a Business Issue', + '1' => 'Acknowledging Climate Change as a Business Issue', + '2' => 'Building Capacity', + '3' => 'Integrating into Operational Decision Making', + '4' => 'Strategic Assessment' + }.freeze + + def details_data(company) + { + name: company.name, + country: company.geography.name, + sector: company.sector.name, + market_cap: 'Large', + isin: company.isin, + sedol: 60, + ca100: company.ca100 ? 'Yes' : 'No', + latest_assessment: company.latest_assessment.questions.group_by { |q| q['level'] }, + levels_descriptions: SECTORS_LEVELS_DESC + } + end + + def emissions_data(company) + company_emissions_data = { + name: company.name, + data: company.cp_assessments.last.emissions + } + + # TODO: calculate real average from all sectors + sectors_average_data = { + name: "#{company.sector.name} sector mean", + data: all_sector_assessments_emissions(company).reduce(&:merge) + } + + sectors_cp_benchmarks = all_sector_benchmarks_emissions(company).map do |_release_date, benchmarks| + { + name: benchmarks.last.scenario, + data: benchmarks.last.emissions + } + end + + [ + company_emissions_data, + sectors_average_data, + sectors_cp_benchmarks + ].flatten + end + + def all_sector_assessments_emissions(company) + ::Company.includes(:cp_assessments).where(sector: company.sector).map(&:cp_assessments).flatten.map(&:emissions) + end + + def all_sector_benchmarks_emissions(company) + company.sector.cp_benchmarks.group_by { |b| b.release_date.to_s } + end + end + end +end diff --git a/app/services/api/companies/emissions.rb b/app/services/api/companies/emissions.rb new file mode 100644 index 000000000..78d36cc9f --- /dev/null +++ b/app/services/api/companies/emissions.rb @@ -0,0 +1,45 @@ +module Api + module Companies + class Emissions + def initialize(company) + @company = company + end + + def get + [ + { + name: @company.name, + data: company_emissions + }, + { + name: "#{@company.sector.name} sector mean", + data: company_sector_mean_emissions + } + ] + end + + private + + def company_emissions + { + 2012 => 111.0, 2013 => 112.0, + 2014 => 101.0, 2015 => 104.0, + 2016 => 102.0, 2017 => 95.0, + 2018 => 96.0, 2019 => 92.0, + 2020 => 90.0, 2021 => 85.0, + 2022 => 83.0, 2023 => 80.0 + } + end + + def company_sector_mean_emissions + { + 2014 => 96.0, 2015 => 94.0, + 2016 => 93.0, 2017 => 90.0, + 2018 => 86.0, 2019 => 82.0, + 2020 => 81.0, 2021 => 75.0, + 2022 => 73.0, 2023 => 70.0 + } + end + end + end +end diff --git a/app/services/api/sectors/companies_count_grouped_by_scenario.rb b/app/services/api/sectors/companies_count_grouped_by_scenario.rb index 2ef33311d..219fecb1b 100644 --- a/app/services/api/sectors/companies_count_grouped_by_scenario.rb +++ b/app/services/api/sectors/companies_count_grouped_by_scenario.rb @@ -3,7 +3,8 @@ module Sectors class CompaniesCountGroupedByScenario CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze - # Returns info of how many Companies from each Sector are in given CP scenario group + # Returns info of how many Companies from each Sector + # are in given CP scenario group. # # [ # { diff --git a/app/services/api/sectors/companies_grouped_by_level.rb b/app/services/api/sectors/companies_grouped_by_level.rb index a5f26793b..bff015421 100644 --- a/app/services/api/sectors/companies_grouped_by_level.rb +++ b/app/services/api/sectors/companies_grouped_by_level.rb @@ -39,7 +39,12 @@ def get # ] # def count - get.map { |level, companies| [level, companies.size] } + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .sort_by { |level, _companies| level } + .map { |level, companies| [level, companies_emissions(companies)] } + .map { |level, companies| [level, companies.size] } end # [ @@ -49,9 +54,22 @@ def count # ] # def emissions - get + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .sort_by { |level, _companies| level } + .map { |level, companies| [level, companies_emissions(companies)] } .map { |_level, companies| companies } - .flatten.map { |company| {name: company[:name], data: company[:emissions]} } + .flatten + .map { |company| series_options(company[:emissions], company[:name]) } + end + + def series_options(data, series_name) + { + name: series_name, + data: data, + lineWidth: 4 + } end private From 7d82914d190bdd5699a918d07ef9777f750b05f4 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Thu, 19 Sep 2019 18:10:06 +0200 Subject: [PATCH 22/56] remove old file --- app/services/api/companies/emissions.rb | 45 ------------------------- 1 file changed, 45 deletions(-) delete mode 100644 app/services/api/companies/emissions.rb diff --git a/app/services/api/companies/emissions.rb b/app/services/api/companies/emissions.rb deleted file mode 100644 index 78d36cc9f..000000000 --- a/app/services/api/companies/emissions.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Api - module Companies - class Emissions - def initialize(company) - @company = company - end - - def get - [ - { - name: @company.name, - data: company_emissions - }, - { - name: "#{@company.sector.name} sector mean", - data: company_sector_mean_emissions - } - ] - end - - private - - def company_emissions - { - 2012 => 111.0, 2013 => 112.0, - 2014 => 101.0, 2015 => 104.0, - 2016 => 102.0, 2017 => 95.0, - 2018 => 96.0, 2019 => 92.0, - 2020 => 90.0, 2021 => 85.0, - 2022 => 83.0, 2023 => 80.0 - } - end - - def company_sector_mean_emissions - { - 2014 => 96.0, 2015 => 94.0, - 2016 => 93.0, 2017 => 90.0, - 2018 => 86.0, 2019 => 82.0, - 2020 => 81.0, 2021 => 75.0, - 2022 => 73.0, 2023 => 70.0 - } - end - end - end -end From ed8f484f23589e88578fbaf100d2194a0de48287 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 09:06:27 +0200 Subject: [PATCH 23/56] refactor chart api services --- app/controllers/companies_controller.rb | 14 +- app/controllers/sectors_controller.rb | 52 +++---- app/services/api/charts/company.rb | 21 ++- app/services/api/charts/sector.rb | 129 ++++++++++++++++++ .../companies_count_grouped_by_scenario.rb | 47 ------- .../api/sectors/companies_grouped_by_level.rb | 88 ------------ 6 files changed, 169 insertions(+), 182 deletions(-) create mode 100644 app/services/api/charts/sector.rb delete mode 100644 app/services/api/sectors/companies_count_grouped_by_scenario.rb delete mode 100644 app/services/api/sectors/companies_grouped_by_level.rb diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 5f0987cb3..426a41a4e 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -5,16 +5,10 @@ def show @company_details = ::Api::Charts::Company.new.details_data(@company) end - # Returns array of 2 elements: - # - company emissions - # - company's sector emissions - # - # @example - # [ - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'Airlines sector mean', data: {'2014' => 114.0, '2015' => 112.0 } } - # ] - # + # Data: Company emissions + # Section: CP + # Type: line chart + # On pages: :show def emissions @company = Company.find(params[:id]) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index 6e2b144e9..fd0baba0c 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -7,57 +7,39 @@ def index def show @sector = Sector.find(params[:id]) - @companies_by_levels = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).get + @companies_by_levels = ::Api::Charts::Sector.new(companies_scope(params)).companies_summaries end # Chart data endpoints - # Returns array of pairs: - # [latest MQ assessment Sector's level, number of Companies on given level] - # - # If params[:id] is given results are scoped to given Sector. - # - # @example: - # [ - # ['0', 13], - # ['1', 63], - # ['2', 61], - # ['3', 71], - # ['4', 63], - # ['4STAR', 6] - # ] - # + # Data: Sectors Levels mapped to number of Companies + # Section: MQ + # Type: pie chart + # On pages: :index, :show def companies_levels - data = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).count + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count render json: data.chart_json end - # Returns array of objects with props: - # - name - Company name - # - data - object representing emissions history ({ year: emission-value }) - # - # If params[:id] is given results are scoped to given Sector. - # - # @example - # [ - # { name: 'WizzAir', data: {} }, - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } - # ] - # + # Data: Companies emissions + # Section: CP + # Type: line chart + # On pages: :show def companies_emissions - data = ::Api::Sectors::CompaniesGroupedByLevel.new(companies_scope(params)).emissions + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions render json: data.chart_json end + # Data: Sectors Companies numbers, grouped by CP Benchmarks from given Sector + # Section: CP + # Type: column chart + # On pages: :index def scenarios - render json: sectors_scenarios_alignments.chart_json - end + data = ::Api::Charts::Sector.new(companies_scope(params)).group_by_cp_benchmark - def sectors_scenarios_alignments - ::Api::Sectors::CompaniesCountGroupedByScenario.new.get + render json: data.chart_json end def companies_scope(params) diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 1f44a7407..4e7a3aef8 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -23,6 +23,16 @@ def details_data(company) } end + # Returns array of 2 elements: + # - company emissions + # - company's sector emissions + # + # @example + # [ + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'Airlines sector mean', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] + # def emissions_data(company) company_emissions_data = { name: company.name, @@ -50,11 +60,18 @@ def emissions_data(company) end def all_sector_assessments_emissions(company) - ::Company.includes(:cp_assessments).where(sector: company.sector).map(&:cp_assessments).flatten.map(&:emissions) + ::Company.includes(:cp_assessments) + .where(sector: company.sector) + .map(&:cp_assessments) + .flatten + .map(&:emissions) end def all_sector_benchmarks_emissions(company) - company.sector.cp_benchmarks.group_by { |b| b.release_date.to_s } + company + .sector + .cp_benchmarks + .group_by { |b| b.release_date.to_s } end end end diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb new file mode 100644 index 000000000..29f118caf --- /dev/null +++ b/app/services/api/charts/sector.rb @@ -0,0 +1,129 @@ +module Api + module Charts + class Sector + CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze + + def initialize(company_scope) + @company_scope = company_scope + end + + # Returns Companies summaries grouped by their latest MQ assessments Sector's levels. + # + # Returns data in multiple series. + # + # @example + # [ + # ['1', + # [ + # { name: 'Air China', status: 'new' }, + # { name: 'China Southern', status: 'new' } + # ] + # ], + # ['3', + # [ + # { name: 'Alaska Air', status: 'up' }, + # { name: 'IAG', status: 'down' } + # ] + # ] + # ] + def companies_summaries + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .sort.to_h + .map { |level, companies| [level, companies_summary(companies)] } + end + + # Returns latest "MQ assessment Sector's levels" mapped to number of Companies in given level + # + # Returns single series of data. + # + # @example + # { '0' => 13, '1' => 63, '2' => 61, '3' => 71, '4' => 63, '4STAR' => 6} + # + def companies_count + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + .map { |level, companies| [level, companies.size] } + .sort.to_h + end + + # Returns companies emissions + # + # Returns data in multiple series. + # + # @example + # [ + # { name: 'WizzAir', data: {} }, + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } + # ] + # + def companies_emissions + @company_scope + .includes(:mq_assessments, :cp_assessments) + .map { |company| company_emissions_series_options(company) } + end + + # Returns Companies stats grouped by CP benchmark. + # + # Returns data in multiple series. + # + # @example + # [ + # { + # name: 'Below 2', + # data: [ ['Coal Mining', 52], ['Steel', 73] ] + # }, + # { + # name: 'Paris', + # data: [ ['Coal Mining', 65], ['Steel', 26] ] + # } + # ] + def group_by_cp_benchmark + CP_SCENARIOS.map do |cp_alignment| + { + name: get_alignment_label(cp_alignment), + data: companies_count_per_sector_cp_scenarios(cp_alignment) + } + end + end + + private + + def get_alignment_label(cp_alignment) + { + below_2: 'Below 2', + exact_2: '2 degrees', + paris: 'Paris', + not_aligned: 'Not aligned', + no_disclosure: 'No disclosure' + }[cp_alignment.to_sym] + end + + def companies_count_per_sector_cp_scenarios(_cp_alignment) + ::Sector.pluck(:id, :name).map do |_sector_id, sector_name| + [sector_name, rand(100)] + end + end + + def company_emissions_series_options(company) + { + name: company.name, + data: company.cp_assessments.last&.emissions, + lineWidth: 4 + } + end + + def companies_summary(companies) + companies.map do |company| + { + name: company.name, + status: company.mq_status + } + end + end + end + end +end diff --git a/app/services/api/sectors/companies_count_grouped_by_scenario.rb b/app/services/api/sectors/companies_count_grouped_by_scenario.rb deleted file mode 100644 index 219fecb1b..000000000 --- a/app/services/api/sectors/companies_count_grouped_by_scenario.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Api - module Sectors - class CompaniesCountGroupedByScenario - CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze - - # Returns info of how many Companies from each Sector - # are in given CP scenario group. - # - # [ - # { - # name: 'Below 2', - # data: [ ['Coal Mining', 52], ['Steel', 73] ] - # }, - # { - # name: 'Paris', - # data: [ ['Coal Mining', 65], ['Steel', 26] ] - # } - # ] - def get - CP_SCENARIOS.map do |cp_alignment| - { - name: get_alignment_label(cp_alignment), - data: companies_count_per_sector_cp_scenarios(cp_alignment) - } - end - end - - private - - def get_alignment_label(cp_alignment) - { - below_2: 'Below 2', - exact_2: '2 degrees', - paris: 'Paris', - not_aligned: 'Not aligned', - no_disclosure: 'No disclosure' - }[cp_alignment.to_sym] - end - - def companies_count_per_sector_cp_scenarios(_cp_alignment) - Sector.pluck(:id, :name).map do |_sector_id, sector_name| - [sector_name, rand(100)] - end - end - end - end -end diff --git a/app/services/api/sectors/companies_grouped_by_level.rb b/app/services/api/sectors/companies_grouped_by_level.rb deleted file mode 100644 index bff015421..000000000 --- a/app/services/api/sectors/companies_grouped_by_level.rb +++ /dev/null @@ -1,88 +0,0 @@ -module Api - module Sectors - class CompaniesGroupedByLevel - def initialize(company_scope) - @company_scope = company_scope - end - - # Returns Companies grouped by their latest MQ assessments Sector's levels - # - # [ - # ['1', - # [ - # { name: 'Air China', emissions: { 2013: 153, 2014: 142 }, status: '' }, - # { name: 'China Southern', emissions: { 2015: 32, 2016: 43 }, status: '' } - # ] - # ], - # ['3', - # [ - # { name: 'Alaska Air', emissions: { }, status: '' }, - # { name: 'IAG', emissions: { }, status: '' } - # ] - # ] - # ] - def get - @company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .sort_by { |level, _companies| level } - .map { |level, companies| [level, companies_emissions(companies)] } - end - - # [ - # ['0', 13], - # ['1', 63], - # ['2', 61], - # ['3', 71], - # ['4', 63], - # ['4STAR', 6] - # ] - # - def count - @company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .sort_by { |level, _companies| level } - .map { |level, companies| [level, companies_emissions(companies)] } - .map { |level, companies| [level, companies.size] } - end - - # [ - # { name: 'WizzAir', data: {} }, - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } - # ] - # - def emissions - @company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .sort_by { |level, _companies| level } - .map { |level, companies| [level, companies_emissions(companies)] } - .map { |_level, companies| companies } - .flatten - .map { |company| series_options(company[:emissions], company[:name]) } - end - - def series_options(data, series_name) - { - name: series_name, - data: data, - lineWidth: 4 - } - end - - private - - def companies_emissions(companies) - companies.map do |company| - { - name: company.name, - status: company.mq_status, - emissions: company.cp_assessments.last&.emissions || {} - } - end - end - end - end -end From ae2ea94a9f46689ce6cea943f5410b9dc726248a Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 10:24:35 +0200 Subject: [PATCH 24/56] simplify company emissions chart data generation --- app/controllers/companies_controller.rb | 3 -- app/services/api/charts/company.rb | 68 +++++++++++++++---------- app/views/companies/show.html.erb | 5 +- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 426a41a4e..48bbe2961 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -14,9 +14,6 @@ def emissions data = ::Api::Charts::Company.new.emissions_data(@company) - # TODO: move to JS - @min_y_axis_value = data.pluck(:data).first.values.min - 20 - render json: data.chart_json end end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 4e7a3aef8..8c3bc7d1b 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -23,33 +23,28 @@ def details_data(company) } end - # Returns array of 2 elements: + # Returns array of following series: # - company emissions - # - company's sector emissions + # - company's sector average emissions + # - company's sector CP benchmarks (scenarios) # # @example # [ - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'Airlines sector mean', data: {'2014' => 114.0, '2015' => 112.0 } } + # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, + # + # { name: 'Airlines sector mean', data: { ... } }, + # + # { name: '2 Degrees (Shift-Improve)', data: { ... } }, + # { name: '2 Degrees (High Efficiency)', data: { ... } }, # ] # def emissions_data(company) - company_emissions_data = { - name: company.name, - data: company.cp_assessments.last.emissions - } + company_emissions_data = emissions_data_series_from_company(company) - # TODO: calculate real average from all sectors - sectors_average_data = { - name: "#{company.sector.name} sector mean", - data: all_sector_assessments_emissions(company).reduce(&:merge) - } + sectors_average_data = emissions_data_series_from_sector(company.sector) - sectors_cp_benchmarks = all_sector_benchmarks_emissions(company).map do |_release_date, benchmarks| - { - name: benchmarks.last.scenario, - data: benchmarks.last.emissions - } + sectors_cp_benchmarks = sector_benchmarks_emissions(company).map do |benchmark| + emissions_data_series_from_sector_benchmark(benchmark) end [ @@ -59,20 +54,39 @@ def emissions_data(company) ].flatten end - def all_sector_assessments_emissions(company) + def sector_benchmarks_emissions(company) + company.sector.cp_benchmarks + end + + def emissions_data_series_from_company(company) + { + name: company.name, + data: company.cp_assessments.last.emissions + } + end + + def emissions_data_series_from_sector_benchmark(cp_benchmark) + { + name: cp_benchmark.scenario, + data: cp_benchmark.emissions + } + end + + # returns average from sector companies + def emissions_data_series_from_sector(sector) + { + name: "#{sector.name} sector mean", + data: all_sector_assessments_emissions(sector).reduce(&:merge) # TODO: calculate real average from all sectors + } + end + + def all_sector_assessments_emissions(sector) ::Company.includes(:cp_assessments) - .where(sector: company.sector) + .where(sector: sector) .map(&:cp_assessments) .flatten .map(&:emissions) end - - def all_sector_benchmarks_emissions(company) - company - .sector - .cp_benchmarks - .group_by { |b| b.release_date.to_s } - end end end end diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index ee1c70091..3c19a3123 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -73,10 +73,7 @@ height: '600px', library: { legend: { - verticalAlign: 'top', - }, - yAxis: { - min: @min_y_axis_value + verticalAlign: 'top' } } )%> From 698877fd9bf81394315a7268ae24ab158464c8fd Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 10:36:04 +0200 Subject: [PATCH 25/56] renaming --- app/services/api/charts/company.rb | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 8c3bc7d1b..48ef0c822 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -10,6 +10,8 @@ class Company }.freeze def details_data(company) + company_latest_assessment = company.latest_assessment.questions.group_by { |q| q['level'] } + { name: company.name, country: company.geography.name, @@ -18,7 +20,7 @@ def details_data(company) isin: company.isin, sedol: 60, ca100: company.ca100 ? 'Yes' : 'No', - latest_assessment: company.latest_assessment.questions.group_by { |q| q['level'] }, + latest_assessment: company_latest_assessment, levels_descriptions: SECTORS_LEVELS_DESC } end @@ -39,22 +41,16 @@ def details_data(company) # ] # def emissions_data(company) - company_emissions_data = emissions_data_series_from_company(company) - - sectors_average_data = emissions_data_series_from_sector(company.sector) - - sectors_cp_benchmarks = sector_benchmarks_emissions(company).map do |benchmark| - emissions_data_series_from_sector_benchmark(benchmark) - end - [ - company_emissions_data, - sectors_average_data, - sectors_cp_benchmarks + emissions_data_series_from_company(company), + emissions_data_series_from_sector(company.sector), + sector_benchmarks(company).map do |benchmark| + emissions_data_series_from_sector_benchmark(benchmark) + end ].flatten end - def sector_benchmarks_emissions(company) + def sector_benchmarks(company) company.sector.cp_benchmarks end From 7b846b0d97f9a06a17e58e81964167f1171bf583 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 13:55:43 +0200 Subject: [PATCH 26/56] calculate sector average emissions for company CP chart --- app/models/company.rb | 4 +++ app/services/api/charts/company.rb | 40 ++++++++++++++++++++++-------- app/services/api/charts/sector.rb | 1 + app/views/companies/show.html.erb | 8 ++++++ app/views/sectors/show.html.erb | 5 +--- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/app/models/company.rb b/app/models/company.rb index f24086241..7d7748200 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -50,4 +50,8 @@ def latest_mq_assessment def to_s name end + + def sector_benchmarks + sector.cp_benchmarks + end end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 48ef0c822..fe163c306 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -44,15 +44,13 @@ def emissions_data(company) [ emissions_data_series_from_company(company), emissions_data_series_from_sector(company.sector), - sector_benchmarks(company).map do |benchmark| + company.sector_benchmarks.map do |benchmark| emissions_data_series_from_sector_benchmark(benchmark) end ].flatten end - def sector_benchmarks(company) - company.sector.cp_benchmarks - end + private def emissions_data_series_from_company(company) { @@ -63,6 +61,7 @@ def emissions_data_series_from_company(company) def emissions_data_series_from_sector_benchmark(cp_benchmark) { + type: 'area', name: cp_benchmark.scenario, data: cp_benchmark.emissions } @@ -72,16 +71,37 @@ def emissions_data_series_from_sector_benchmark(cp_benchmark) def emissions_data_series_from_sector(sector) { name: "#{sector.name} sector mean", - data: all_sector_assessments_emissions(sector).reduce(&:merge) # TODO: calculate real average from all sectors + data: sector_average_emissions(sector) } end - def all_sector_assessments_emissions(sector) - ::Company.includes(:cp_assessments) - .where(sector: sector) + # Returns average emissions history for given Sector. + # For each year, average value is calculated from all available companies emissions, + # up to last reported year. + # + # @example {'2014' => 111.0, '2015' => 112.0 } + # + def sector_average_emissions(sector) + all_sector_emissions = sector_all_emissions(sector) + all_years = all_sector_emissions.map(&:keys).flatten.map(&:to_i).uniq + + last_reported_year = Time.new.year + years = (all_years.min..last_reported_year).map.to_a + + sector_average_for_year = lambda do |year| + company_emissions = all_sector_emissions.map { |emissions| emissions[year.to_s] }.compact + (company_emissions.sum / company_emissions.count).round(2) + end + + years.map { |year| [year, sector_average_for_year.call(year)] }.to_h + end + + def sector_all_emissions(sector) + sector + .companies + .includes(:cp_assessments) .map(&:cp_assessments) - .flatten - .map(&:emissions) + .flatten.map(&:emissions) end end end diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 29f118caf..82ec72956 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -119,6 +119,7 @@ def company_emissions_series_options(company) def companies_summary(companies) companies.map do |company| { + id: company.id, name: company.name, status: company.mq_status } diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index 3c19a3123..1e2832ddb 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -74,6 +74,14 @@ library: { legend: { verticalAlign: 'top' + }, + plotOptions: { + area: { + fillOpacity: 0.3, + marker: { + enabled: false + } + } } } )%> diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 9f9c7b061..7dbffe585 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -34,7 +34,7 @@
    <%- companies.each do |company| %> -
  • <%= company[:name] %> [<%= company[:status] %>]
  • +
  • <%= link_to company[:name], company_path(company[:id]) %> [<%= company[:status] %>]
  • <%- end %>
@@ -53,9 +53,6 @@ library: { legend: { verticalAlign: 'top', - }, - yAxis: { - min: @min_y_axis_value } } )%> From e44c6d5dfc3518329621dd7758c605911b6b4e0f Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 14:51:48 +0200 Subject: [PATCH 27/56] rename routes for charts --- app/controllers/companies_controller.rb | 2 +- app/controllers/sectors_controller.rb | 10 +++++----- app/services/api/charts/company.rb | 20 +++++++++++--------- app/views/companies/show.html.erb | 3 ++- app/views/sectors/index.html.erb | 4 ++-- app/views/sectors/show.html.erb | 13 +++++++++++-- config/routes.rb | 10 +++++----- 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 48bbe2961..5b9b74504 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -9,7 +9,7 @@ def show # Section: CP # Type: line chart # On pages: :show - def emissions + def emissions_chart_data @company = Company.find(params[:id]) data = ::Api::Charts::Company.new.emissions_data(@company) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index fd0baba0c..b1eb2ef37 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -12,11 +12,11 @@ def show # Chart data endpoints - # Data: Sectors Levels mapped to number of Companies + # Data: Sectors Companies number, grouped by Levels # Section: MQ # Type: pie chart # On pages: :index, :show - def companies_levels + def levels_chart_data data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count render json: data.chart_json @@ -26,17 +26,17 @@ def companies_levels # Section: CP # Type: line chart # On pages: :show - def companies_emissions + def emissions_chart_data data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions render json: data.chart_json end - # Data: Sectors Companies numbers, grouped by CP Benchmarks from given Sector + # Data: Sector Companies numbers, grouped by CP Benchmarks from given Sector # Section: CP # Type: column chart # On pages: :index - def scenarios + def benchmarks_chart_data data = ::Api::Charts::Sector.new(companies_scope(params)).group_by_cp_benchmark render json: data.chart_json diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index fe163c306..96cf0dfca 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -10,8 +10,6 @@ class Company }.freeze def details_data(company) - company_latest_assessment = company.latest_assessment.questions.group_by { |q| q['level'] } - { name: company.name, country: company.geography.name, @@ -20,11 +18,15 @@ def details_data(company) isin: company.isin, sedol: 60, ca100: company.ca100 ? 'Yes' : 'No', - latest_assessment: company_latest_assessment, + latest_assessment: questions_by_level(company), levels_descriptions: SECTORS_LEVELS_DESC } end + def questions_by_level(company) + company.latest_assessment.questions.group_by { |q| q['level'] } + end + # Returns array of following series: # - company emissions # - company's sector average emissions @@ -42,24 +44,24 @@ def details_data(company) # def emissions_data(company) [ - emissions_data_series_from_company(company), - emissions_data_series_from_sector(company.sector), + data_series_from_company(company), + data_series_from_sector(company.sector), company.sector_benchmarks.map do |benchmark| - emissions_data_series_from_sector_benchmark(benchmark) + data_series_from_sector_benchmark(benchmark) end ].flatten end private - def emissions_data_series_from_company(company) + def data_series_from_company(company) { name: company.name, data: company.cp_assessments.last.emissions } end - def emissions_data_series_from_sector_benchmark(cp_benchmark) + def data_series_from_sector_benchmark(cp_benchmark) { type: 'area', name: cp_benchmark.scenario, @@ -68,7 +70,7 @@ def emissions_data_series_from_sector_benchmark(cp_benchmark) end # returns average from sector companies - def emissions_data_series_from_sector(sector) + def data_series_from_sector(sector) { name: "#{sector.name} sector mean", data: sector_average_emissions(sector) diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index 1e2832ddb..efb27b707 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -64,11 +64,12 @@
+

Carbon Performance <%= @company.name %>

Carbon Performance alignment of <%= @company.name %> with the Paris agreement benchmarks.

<%= line_chart( - emissions_company_path(@company.id), + emissions_chart_data_company_path(@company.id), width: '800px', height: '600px', library: { diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index bff7a4a3d..2aeb72870 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -14,7 +14,7 @@ Companies:
<%= pie_chart( - companies_levels_sectors_path, + levels_chart_data_sectors_path, donut: true, library: { tooltip: { enabled: false }, @@ -41,7 +41,7 @@ Companies:
<%= column_chart( - scenarios_sectors_path, + benchmarks_chart_data_sectors_path, stacked: true, width: '1200px', library: { diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 7dbffe585..5df68e587 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -8,7 +8,7 @@
<%= pie_chart( - companies_levels_sector_path(@sector.id), + levels_chart_data_sector_path(@sector.id), donut: true, library: { tooltip: { enabled: false }, @@ -44,15 +44,24 @@
+

Carbon Performance: <%= @sector.name %>

<%= line_chart( - companies_emissions_sector_path(@sector.id), + emissions_chart_data_sector_path(@sector.id), width: '1200px', height: '800px', library: { legend: { verticalAlign: 'top', + }, + plotOptions: { + area: { + fillOpacity: 0.3, + marker: { + enabled: false + } + } } } )%> diff --git a/config/routes.rb b/config/routes.rb index 65671599c..ed246d8f5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,17 +2,17 @@ # TPI resources :sectors, only: [:show, :index] do collection do - get :companies_levels - get :scenarios + get :levels_chart_data + get :benchmarks_chart_data end member do - get :companies_levels - get :companies_emissions + get :levels_chart_data + get :emissions_chart_data end end resources :companies, only: [:show] do member do - get :emissions + get :emissions_chart_data end end From e3cf35415d02f94b0e0e85d71b4c00617bd12088 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 20 Sep 2019 15:03:50 +0200 Subject: [PATCH 28/56] extract common charts options to ChartHelper --- app/helpers/chart_helper.rb | 34 +++++++++++++++++++++++++++++++ app/views/companies/show.html.erb | 14 +------------ app/views/sectors/index.html.erb | 22 ++------------------ app/views/sectors/show.html.erb | 25 ++--------------------- 4 files changed, 39 insertions(+), 56 deletions(-) create mode 100644 app/helpers/chart_helper.rb diff --git a/app/helpers/chart_helper.rb b/app/helpers/chart_helper.rb new file mode 100644 index 000000000..92bd6daa4 --- /dev/null +++ b/app/helpers/chart_helper.rb @@ -0,0 +1,34 @@ +module ChartHelper + # defaults chart options (Highcharts) + def default_chart_options + { + legend: { + verticalAlign: 'top' + }, + plotOptions: { + area: { + fillOpacity: 0.3, + marker: { + enabled: false + } + }, + column: { + stacking: 'percent' + } + } + } + end + + def mq_sector_pie_chart_options + { + tooltip: {enabled: false}, + plotOptions: { + pie: { + dataLabels: { + format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' + } + } + } + } + end +end diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb index efb27b707..4fbc83844 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/companies/show.html.erb @@ -72,18 +72,6 @@ emissions_chart_data_company_path(@company.id), width: '800px', height: '600px', - library: { - legend: { - verticalAlign: 'top' - }, - plotOptions: { - area: { - fillOpacity: 0.3, - marker: { - enabled: false - } - } - } - } + library: default_chart_options )%>
\ No newline at end of file diff --git a/app/views/sectors/index.html.erb b/app/views/sectors/index.html.erb index 2aeb72870..94dd94dcb 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/sectors/index.html.erb @@ -16,16 +16,7 @@ Companies: <%= pie_chart( levels_chart_data_sectors_path, donut: true, - library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' - } - } - } - } + library: mq_sector_pie_chart_options ) %>
@@ -44,15 +35,6 @@ Companies: benchmarks_chart_data_sectors_path, stacked: true, width: '1200px', - library: { - legend: { - verticalAlign: 'top', - }, - plotOptions: { - column: { - stacking: 'percent' - } - }, - } + library: default_chart_options ) %>
diff --git a/app/views/sectors/show.html.erb b/app/views/sectors/show.html.erb index 5df68e587..e3f0ba835 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/sectors/show.html.erb @@ -10,16 +10,7 @@ <%= pie_chart( levels_chart_data_sector_path(@sector.id), donut: true, - library: { - tooltip: { enabled: false }, - plotOptions: { - pie: { - dataLabels: { - format: 'Level {point.name}
{point.y} companies
{point.percentage:.1f}%' - } - } - } - } + library: mq_sector_pie_chart_options ) %>
@@ -51,18 +42,6 @@ emissions_chart_data_sector_path(@sector.id), width: '1200px', height: '800px', - library: { - legend: { - verticalAlign: 'top', - }, - plotOptions: { - area: { - fillOpacity: 0.3, - marker: { - enabled: false - } - } - } - } + library: default_chart_options )%>
From 9b92c945507634809d6c039f07d097a0559b9800 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 10:30:39 +0200 Subject: [PATCH 29/56] Set company in Api::Charts::Company constructor --- app/controllers/companies_controller.rb | 4 +- app/services/api/charts/company.rb | 81 ++++++++++++++----------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 5b9b74504..cfff9cbc4 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -2,7 +2,7 @@ class CompaniesController < ApplicationController def show @company = Company.find(params[:id]) - @company_details = ::Api::Charts::Company.new.details_data(@company) + @company_details = ::Api::Charts::Company.new(@company).details_data end # Data: Company emissions @@ -12,7 +12,7 @@ def show def emissions_chart_data @company = Company.find(params[:id]) - data = ::Api::Charts::Company.new.emissions_data(@company) + data = ::Api::Charts::Company.new(@company).emissions_data render json: data.chart_json end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 96cf0dfca..fd51a4ece 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -1,6 +1,8 @@ module Api module Charts class Company + attr_reader :company + SECTORS_LEVELS_DESC = { '0' => 'Unaware of Climate Change as a Business Issue', '1' => 'Acknowledging Climate Change as a Business Issue', @@ -9,7 +11,11 @@ class Company '4' => 'Strategic Assessment' }.freeze - def details_data(company) + def initialize(company) + @company = company + end + + def details_data { name: company.name, country: company.geography.name, @@ -18,15 +24,11 @@ def details_data(company) isin: company.isin, sedol: 60, ca100: company.ca100 ? 'Yes' : 'No', - latest_assessment: questions_by_level(company), + latest_assessment: questions_by_level, levels_descriptions: SECTORS_LEVELS_DESC } end - def questions_by_level(company) - company.latest_assessment.questions.group_by { |q| q['level'] } - end - # Returns array of following series: # - company emissions # - company's sector average emissions @@ -42,64 +44,73 @@ def questions_by_level(company) # { name: '2 Degrees (High Efficiency)', data: { ... } }, # ] # - def emissions_data(company) + def emissions_data [ - data_series_from_company(company), - data_series_from_sector(company.sector), - company.sector_benchmarks.map do |benchmark| - data_series_from_sector_benchmark(benchmark) - end + emissions_data_from_company, + emissions_data_from_sector, + emissions_data_from_sector_benchmarks ].flatten end private - def data_series_from_company(company) + def questions_by_level + company.latest_assessment.questions.group_by { |q| q['level'] } + end + + def emissions_data_from_company { name: company.name, data: company.cp_assessments.last.emissions } end - def data_series_from_sector_benchmark(cp_benchmark) + def emissions_data_from_sector { - type: 'area', - name: cp_benchmark.scenario, - data: cp_benchmark.emissions + name: "#{company.sector.name} sector mean", + data: sector_average_emissions } end - # returns average from sector companies - def data_series_from_sector(sector) - { - name: "#{sector.name} sector mean", - data: sector_average_emissions(sector) - } + def emissions_data_from_sector_benchmarks + company.sector_benchmarks.map do |benchmark| + { + type: 'area', + name: benchmark.scenario, + data: benchmark.emissions + } + end end # Returns average emissions history for given Sector. - # For each year, average value is calculated from all available companies emissions, - # up to last reported year. + # For each year, average value is calculated from all available + # companies emissions, up to last reported year. # # @example {'2014' => 111.0, '2015' => 112.0 } # - def sector_average_emissions(sector) - all_sector_emissions = sector_all_emissions(sector) - all_years = all_sector_emissions.map(&:keys).flatten.map(&:to_i).uniq + def sector_average_emissions + all_years = sector_all_emissions.map(&:keys).flatten.map(&:to_i).uniq last_reported_year = Time.new.year years = (all_years.min..last_reported_year).map.to_a - sector_average_for_year = lambda do |year| - company_emissions = all_sector_emissions.map { |emissions| emissions[year.to_s] }.compact - (company_emissions.sum / company_emissions.count).round(2) - end + years + .map { |year| [year, sector_average_emission_for_year(year)] } + .to_h + end + + # Returns average emission from all Companies from single year + def sector_average_emission_for_year(year) + company_emissions = sector_all_emissions + .map { |emissions| emissions[year.to_s] } + .compact - years.map { |year| [year, sector_average_for_year.call(year)] }.to_h + (company_emissions.sum / company_emissions.count).round(2) end - def sector_all_emissions(sector) - sector + # Returns array of emissions ({ year => value }) from all Companies from current Sector + def sector_all_emissions + @sector_all_emissions ||= company.sector .companies .includes(:cp_assessments) .map(&:cp_assessments) From a8063fc356f88cd5313929a386db0c482ba838a7 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 12:02:34 +0200 Subject: [PATCH 30/56] add CP benchmarks to companies emissions chart --- app/controllers/sectors_controller.rb | 2 +- app/services/api/charts/company.rb | 1 + app/services/api/charts/sector.rb | 26 ++++++++++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index b1eb2ef37..ec8a6483c 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -27,7 +27,7 @@ def levels_chart_data # Type: line chart # On pages: :show def emissions_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions_data render json: data.chart_json end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index fd51a4ece..d594e855a 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -76,6 +76,7 @@ def emissions_data_from_sector_benchmarks company.sector_benchmarks.map do |benchmark| { type: 'area', + fillOpacity: 0.1, name: benchmark.scenario, data: benchmark.emissions } diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 82ec72956..cc290c742 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -60,10 +60,11 @@ def companies_count # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } # ] # - def companies_emissions - @company_scope - .includes(:mq_assessments, :cp_assessments) - .map { |company| company_emissions_series_options(company) } + def companies_emissions_data + [ + emissions_data_from_companies, + emissions_data_from_sector_benchmarks + ].flatten end # Returns Companies stats grouped by CP benchmark. @@ -92,6 +93,23 @@ def group_by_cp_benchmark private + def emissions_data_from_companies + @company_scope + .includes(:mq_assessments, :cp_assessments) + .map { |company| company_emissions_series_options(company) } + end + + def emissions_data_from_sector_benchmarks + @company_scope.first.sector_benchmarks.map do |benchmark| + { + type: 'area', + fillOpacity: 0.1, + name: benchmark.scenario, + data: benchmark.emissions + } + end + end + def get_alignment_label(cp_alignment) { below_2: 'Below 2', From 52f1652d99e3769d780e12eedb5c6543c392c32d Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 12:34:53 +0200 Subject: [PATCH 31/56] companies_scope in sectors contoller can be private --- app/controllers/sectors_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb index ec8a6483c..accdf5241 100644 --- a/app/controllers/sectors_controller.rb +++ b/app/controllers/sectors_controller.rb @@ -42,6 +42,8 @@ def benchmarks_chart_data render json: data.chart_json end + private + def companies_scope(params) if params[:id] Sector.find(params[:id]).companies From 59c6117a69da56c98eb6bfc5a08488b3ae29323e Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 12:35:27 +0200 Subject: [PATCH 32/56] extract questions_by_level to MQ::Assessment model --- app/models/mq/assessment.rb | 4 ++++ app/services/api/charts/company.rb | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/mq/assessment.rb b/app/models/mq/assessment.rb index 542de88a7..2d1aca3c0 100644 --- a/app/models/mq/assessment.rb +++ b/app/models/mq/assessment.rb @@ -61,5 +61,9 @@ def questions=(value) def find_answer_by_key(key) questions.find { |q| q.key == key }.answer end + + def questions_by_level + questions.group_by { |q| q['level'] } + end end end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index d594e855a..e198bf68b 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -24,7 +24,7 @@ def details_data isin: company.isin, sedol: 60, ca100: company.ca100 ? 'Yes' : 'No', - latest_assessment: questions_by_level, + latest_assessment: company.latest_assessment.questions_by_level, levels_descriptions: SECTORS_LEVELS_DESC } end @@ -54,10 +54,6 @@ def emissions_data private - def questions_by_level - company.latest_assessment.questions.group_by { |q| q['level'] } - end - def emissions_data_from_company { name: company.name, From 2c40777ff5a7d2ef835a704d0bb705b426c68f7b Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 12:47:14 +0200 Subject: [PATCH 33/56] extrac Api::Presenters::Company --- app/controllers/companies_controller.rb | 2 +- app/services/api/charts/company.rb | 34 ++++--------------------- app/services/api/presenters/company.rb | 31 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 app/services/api/presenters/company.rb diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index cfff9cbc4..532d1df92 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -2,7 +2,7 @@ class CompaniesController < ApplicationController def show @company = Company.find(params[:id]) - @company_details = ::Api::Charts::Company.new(@company).details_data + @company_details = ::Api::Presenters::Company.new(@company).company_details end # Data: Company emissions diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index e198bf68b..ce0f2e0c1 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -1,34 +1,10 @@ module Api module Charts class Company - attr_reader :company - - SECTORS_LEVELS_DESC = { - '0' => 'Unaware of Climate Change as a Business Issue', - '1' => 'Acknowledging Climate Change as a Business Issue', - '2' => 'Building Capacity', - '3' => 'Integrating into Operational Decision Making', - '4' => 'Strategic Assessment' - }.freeze - def initialize(company) @company = company end - def details_data - { - name: company.name, - country: company.geography.name, - sector: company.sector.name, - market_cap: 'Large', - isin: company.isin, - sedol: 60, - ca100: company.ca100 ? 'Yes' : 'No', - latest_assessment: company.latest_assessment.questions_by_level, - levels_descriptions: SECTORS_LEVELS_DESC - } - end - # Returns array of following series: # - company emissions # - company's sector average emissions @@ -56,20 +32,20 @@ def emissions_data def emissions_data_from_company { - name: company.name, - data: company.cp_assessments.last.emissions + name: @company.name, + data: @company.cp_assessments.last.emissions } end def emissions_data_from_sector { - name: "#{company.sector.name} sector mean", + name: "#{@company.sector.name} sector mean", data: sector_average_emissions } end def emissions_data_from_sector_benchmarks - company.sector_benchmarks.map do |benchmark| + @company.sector_benchmarks.map do |benchmark| { type: 'area', fillOpacity: 0.1, @@ -107,7 +83,7 @@ def sector_average_emission_for_year(year) # Returns array of emissions ({ year => value }) from all Companies from current Sector def sector_all_emissions - @sector_all_emissions ||= company.sector + @sector_all_emissions ||= @company.sector .companies .includes(:cp_assessments) .map(&:cp_assessments) diff --git a/app/services/api/presenters/company.rb b/app/services/api/presenters/company.rb new file mode 100644 index 000000000..e398e951b --- /dev/null +++ b/app/services/api/presenters/company.rb @@ -0,0 +1,31 @@ +module Api + module Presenters + class Company + SECTORS_LEVELS_DESC = { + '0' => 'Unaware of Climate Change as a Business Issue', + '1' => 'Acknowledging Climate Change as a Business Issue', + '2' => 'Building Capacity', + '3' => 'Integrating into Operational Decision Making', + '4' => 'Strategic Assessment' + }.freeze + + def initialize(company) + @company = company + end + + def company_details + { + name: @company.name, + country: @company.geography.name, + sector: @company.sector.name, + market_cap: 'Large', + isin: @company.isin, + sedol: 60, + ca100: @company.ca100 ? 'Yes' : 'No', + latest_assessment: @company.latest_assessment.questions_by_level, + levels_descriptions: SECTORS_LEVELS_DESC + } + end + end + end +end From a037323b58abcda3d4074b5d97d505be8232a275 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 23 Sep 2019 12:58:39 +0200 Subject: [PATCH 34/56] move everything under :tpi namespace --- app/controllers/companies_controller.rb | 19 ------- app/controllers/sectors_controller.rb | 54 -------------------- app/controllers/tpi/companies_controller.rb | 21 ++++++++ app/controllers/tpi/sectors_controller.rb | 56 +++++++++++++++++++++ app/views/{ => tpi}/companies/show.html.erb | 5 +- app/views/{ => tpi}/sectors/index.html.erb | 8 +-- app/views/{ => tpi}/sectors/show.html.erb | 9 ++-- config/routes.rb | 27 +++++----- 8 files changed, 101 insertions(+), 98 deletions(-) delete mode 100644 app/controllers/companies_controller.rb delete mode 100644 app/controllers/sectors_controller.rb create mode 100644 app/controllers/tpi/companies_controller.rb create mode 100644 app/controllers/tpi/sectors_controller.rb rename app/views/{ => tpi}/companies/show.html.erb (94%) rename app/views/{ => tpi}/sectors/index.html.erb (75%) rename app/views/{ => tpi}/sectors/show.html.erb (77%) diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb deleted file mode 100644 index 532d1df92..000000000 --- a/app/controllers/companies_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -class CompaniesController < ApplicationController - def show - @company = Company.find(params[:id]) - - @company_details = ::Api::Presenters::Company.new(@company).company_details - end - - # Data: Company emissions - # Section: CP - # Type: line chart - # On pages: :show - def emissions_chart_data - @company = Company.find(params[:id]) - - data = ::Api::Charts::Company.new(@company).emissions_data - - render json: data.chart_json - end -end diff --git a/app/controllers/sectors_controller.rb b/app/controllers/sectors_controller.rb deleted file mode 100644 index accdf5241..000000000 --- a/app/controllers/sectors_controller.rb +++ /dev/null @@ -1,54 +0,0 @@ -class SectorsController < ApplicationController - def index - @sectors_names = Sector.order(:name).pluck(:id, :name) - @companies_names = Company.limit(50).pluck(:id, :name) - end - - def show - @sector = Sector.find(params[:id]) - - @companies_by_levels = ::Api::Charts::Sector.new(companies_scope(params)).companies_summaries - end - - # Chart data endpoints - - # Data: Sectors Companies number, grouped by Levels - # Section: MQ - # Type: pie chart - # On pages: :index, :show - def levels_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count - - render json: data.chart_json - end - - # Data: Companies emissions - # Section: CP - # Type: line chart - # On pages: :show - def emissions_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions_data - - render json: data.chart_json - end - - # Data: Sector Companies numbers, grouped by CP Benchmarks from given Sector - # Section: CP - # Type: column chart - # On pages: :index - def benchmarks_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).group_by_cp_benchmark - - render json: data.chart_json - end - - private - - def companies_scope(params) - if params[:id] - Sector.find(params[:id]).companies - else - Company - end - end -end diff --git a/app/controllers/tpi/companies_controller.rb b/app/controllers/tpi/companies_controller.rb new file mode 100644 index 000000000..8b5838f36 --- /dev/null +++ b/app/controllers/tpi/companies_controller.rb @@ -0,0 +1,21 @@ +module Tpi + class CompaniesController < ApplicationController + def show + @company = Company.find(params[:id]) + + @company_details = ::Api::Presenters::Company.new(@company).company_details + end + + # Data: Company emissions + # Section: CP + # Type: line chart + # On pages: :show + def emissions_chart_data + @company = Company.find(params[:id]) + + data = ::Api::Charts::Company.new(@company).emissions_data + + render json: data.chart_json + end + end +end diff --git a/app/controllers/tpi/sectors_controller.rb b/app/controllers/tpi/sectors_controller.rb new file mode 100644 index 000000000..ff41ccdf2 --- /dev/null +++ b/app/controllers/tpi/sectors_controller.rb @@ -0,0 +1,56 @@ +module Tpi + class SectorsController < ApplicationController + def index + @sectors_names = Sector.order(:name).pluck(:id, :name) + @companies_names = Company.limit(50).pluck(:id, :name) + end + + def show + @sector = Sector.find(params[:id]) + + @companies_by_levels = ::Api::Charts::Sector.new(companies_scope(params)).companies_summaries + end + + # Chart data endpoints + + # Data: Sectors Companies number, grouped by Levels + # Section: MQ + # Type: pie chart + # On pages: :index, :show + def levels_chart_data + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count + + render json: data.chart_json + end + + # Data: Companies emissions + # Section: CP + # Type: line chart + # On pages: :show + def emissions_chart_data + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_emissions_data + + render json: data.chart_json + end + + # Data: Sector Companies numbers, grouped by CP Benchmarks from given Sector + # Section: CP + # Type: column chart + # On pages: :index + def benchmarks_chart_data + data = ::Api::Charts::Sector.new(companies_scope(params)).group_by_cp_benchmark + + render json: data.chart_json + end + + private + + def companies_scope(params) + if params[:id] + Sector.find(params[:id]).companies + else + Company + end + end + end +end diff --git a/app/views/companies/show.html.erb b/app/views/tpi/companies/show.html.erb similarity index 94% rename from app/views/companies/show.html.erb rename to app/views/tpi/companies/show.html.erb index 4fbc83844..567f631f4 100644 --- a/app/views/companies/show.html.erb +++ b/app/views/tpi/companies/show.html.erb @@ -1,5 +1,5 @@

Company Details

-<%= link_to 'back to all Sectors', sectors_path %> +<%= link_to 'back to all Sectors', tpi_sectors_path %>
@@ -64,12 +64,11 @@
-

Carbon Performance <%= @company.name %>

Carbon Performance alignment of <%= @company.name %> with the Paris agreement benchmarks.

<%= line_chart( - emissions_chart_data_company_path(@company.id), + emissions_chart_data_tpi_company_path(@company.id), width: '800px', height: '600px', library: default_chart_options diff --git a/app/views/sectors/index.html.erb b/app/views/tpi/sectors/index.html.erb similarity index 75% rename from app/views/sectors/index.html.erb rename to app/views/tpi/sectors/index.html.erb index 94dd94dcb..951eef837 100644 --- a/app/views/sectors/index.html.erb +++ b/app/views/tpi/sectors/index.html.erb @@ -1,11 +1,11 @@

Sectors dashboard

Sectors: -<%= @sectors_names.map {|s| link_to s.last, sector_path(s.first)}.join(', ').html_safe %> +<%= @sectors_names.map {|s| link_to s.last, tpi_sector_path(s.first)}.join(', ').html_safe %>

Companies: -<%= @companies_names.map {|c| link_to c.last, company_path(c.first)}.join(', ').html_safe %> +<%= @companies_names.map {|c| link_to c.last, tpi_company_path(c.first)}.join(', ').html_safe %>


@@ -14,7 +14,7 @@ Companies:
<%= pie_chart( - levels_chart_data_sectors_path, + levels_chart_data_tpi_sectors_path, donut: true, library: mq_sector_pie_chart_options ) %> @@ -32,7 +32,7 @@ Companies:
<%= column_chart( - benchmarks_chart_data_sectors_path, + benchmarks_chart_data_tpi_sectors_path, stacked: true, width: '1200px', library: default_chart_options diff --git a/app/views/sectors/show.html.erb b/app/views/tpi/sectors/show.html.erb similarity index 77% rename from app/views/sectors/show.html.erb rename to app/views/tpi/sectors/show.html.erb index e3f0ba835..377aa72a3 100644 --- a/app/views/sectors/show.html.erb +++ b/app/views/tpi/sectors/show.html.erb @@ -1,5 +1,5 @@

Sector Details

-<%= link_to 'back to all Sectors', sectors_path %> +<%= link_to 'back to all Sectors', tpi_sectors_path %>
@@ -8,7 +8,7 @@
<%= pie_chart( - levels_chart_data_sector_path(@sector.id), + levels_chart_data_tpi_sector_path(@sector.id), donut: true, library: mq_sector_pie_chart_options ) %> @@ -25,7 +25,7 @@
    <%- companies.each do |company| %> -
  • <%= link_to company[:name], company_path(company[:id]) %> [<%= company[:status] %>]
  • +
  • <%= link_to company[:name], tpi_company_path(company[:id]) %> [<%= company[:status] %>]
  • <%- end %>
@@ -35,11 +35,10 @@
-

Carbon Performance: <%= @sector.name %>

<%= line_chart( - emissions_chart_data_sector_path(@sector.id), + emissions_chart_data_tpi_sector_path(@sector.id), width: '1200px', height: '800px', library: default_chart_options diff --git a/config/routes.rb b/config/routes.rb index ed246d8f5..954dcf5cc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,18 +1,19 @@ Rails.application.routes.draw do - # TPI - resources :sectors, only: [:show, :index] do - collection do - get :levels_chart_data - get :benchmarks_chart_data + namespace :tpi do + resources :sectors, only: [:show, :index] do + collection do + get :levels_chart_data + get :benchmarks_chart_data + end + member do + get :levels_chart_data + get :emissions_chart_data + end end - member do - get :levels_chart_data - get :emissions_chart_data - end - end - resources :companies, only: [:show] do - member do - get :emissions_chart_data + resources :companies, only: [:show] do + member do + get :emissions_chart_data + end end end From b3915cee0ee96e582934cf3d11733113c6eb5db0 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Tue, 24 Sep 2019 20:31:34 +0200 Subject: [PATCH 35/56] company cp|mq assessments are ordered with latest first (by assessment date) --- app/admin/companies.rb | 4 ++-- app/models/company.rb | 4 ++++ app/services/api/charts/company.rb | 12 ++++++++++-- app/services/api/charts/sector.rb | 18 ++++++++++-------- app/services/api/presenters/company.rb | 2 +- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/app/admin/companies.rb b/app/admin/companies.rb index c6e151439..7892bb48f 100644 --- a/app/admin/companies.rb +++ b/app/admin/companies.rb @@ -56,7 +56,7 @@ 'No Management Quality Assessments for this company yet' end else - resource.mq_assessments.latest_first.decorate.map do |a| + resource.mq_assessments.decorate.map do |a| panel a.title, class: 'mq_assessment' do attributes_table_for a do row :level, &:level_tag @@ -82,7 +82,7 @@ 'No Carbon Performance Assessments for this company yet' end else - resource.cp_assessments.latest_first.decorate.map do |a| + resource.cp_assessments.decorate.map do |a| div class: 'panel benchmark' do attributes_table_for a do row :publication_date diff --git a/app/models/company.rb b/app/models/company.rb index 7d7748200..353370eb6 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -47,6 +47,10 @@ def latest_mq_assessment mq_assessments.order(:assessment_date).last end + def latest_cp_assessment + cp_assessments.first + end + def to_s name end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index ce0f2e0c1..7c0e16169 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -33,7 +33,7 @@ def emissions_data def emissions_data_from_company { name: @company.name, - data: @company.cp_assessments.last.emissions + data: @company.latest_cp_assessment.emissions } end @@ -44,8 +44,16 @@ def emissions_data_from_sector } end + # if we have benchmarks available for + # - 04.2017 + # .. assessment date is 06.2017 => we take 04.2017 benchmarks + # - 05.2018 + # .. assessment date is 06.2018 => we take 05.2018 benchmarks + # + # for the chart sector benchmarks should be only + # for the last date before the CP::Assessment date def emissions_data_from_sector_benchmarks - @company.sector_benchmarks.map do |benchmark| + @company.sector.cp_benchmarks.map do |benchmark| { type: 'area', fillOpacity: 0.1, diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index cc290c742..1af285251 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -27,11 +27,9 @@ def initialize(company_scope) # ] # ] def companies_summaries - @company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } - .sort.to_h + companies_grouped_by_latest_assessment_level .map { |level, companies| [level, companies_summary(companies)] } + .sort.to_h end # Returns latest "MQ assessment Sector's levels" mapped to number of Companies in given level @@ -42,9 +40,7 @@ def companies_summaries # { '0' => 13, '1' => 63, '2' => 61, '3' => 71, '4' => 63, '4STAR' => 6} # def companies_count - @company_scope - .includes(:mq_assessments) - .group_by { |company| company.mq_assessments.order(:assessment_date).first.level } + companies_grouped_by_latest_assessment_level .map { |level, companies| [level, companies.size] } .sort.to_h end @@ -93,6 +89,12 @@ def group_by_cp_benchmark private + def companies_grouped_by_latest_assessment_level + @company_scope + .includes(:mq_assessments) + .group_by { |company| company.latest_mq_assessment.level } + end + def emissions_data_from_companies @company_scope .includes(:mq_assessments, :cp_assessments) @@ -129,7 +131,7 @@ def companies_count_per_sector_cp_scenarios(_cp_alignment) def company_emissions_series_options(company) { name: company.name, - data: company.cp_assessments.last&.emissions, + data: company.latest_cp_assessment.emissions, lineWidth: 4 } end diff --git a/app/services/api/presenters/company.rb b/app/services/api/presenters/company.rb index e398e951b..541c6e1c0 100644 --- a/app/services/api/presenters/company.rb +++ b/app/services/api/presenters/company.rb @@ -22,7 +22,7 @@ def company_details isin: @company.isin, sedol: 60, ca100: @company.ca100 ? 'Yes' : 'No', - latest_assessment: @company.latest_assessment.questions_by_level, + latest_assessment: @company.latest_mq_assessment.questions_by_level, levels_descriptions: SECTORS_LEVELS_DESC } end From fc53e7015534e5bf2f366dc0223e77ba2ab9da73 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 27 Sep 2019 00:13:15 +0200 Subject: [PATCH 36/56] calculate sector avg emissions for company latest CP assessments (not all of them) --- app/services/api/charts/company.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 7c0e16169..5d1cc639a 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -94,8 +94,7 @@ def sector_all_emissions @sector_all_emissions ||= @company.sector .companies .includes(:cp_assessments) - .map(&:cp_assessments) - .flatten.map(&:emissions) + .flat_map { |c| c.latest_cp_assessment.emissions } end end end From a6e1c042497081a5fa0e160413455a378e8fe3b6 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 27 Sep 2019 12:49:45 +0200 Subject: [PATCH 37/56] remove ordering from scope --- app/models/company.rb | 10 +++++----- spec/models/company_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/models/company.rb b/app/models/company.rb index 353370eb6..b810bdd7b 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -43,16 +43,16 @@ class Company < ApplicationRecord validates_presence_of :name, :slug, :isin, :size validates_uniqueness_of :slug, :isin, :name + def to_s + name + end + def latest_mq_assessment mq_assessments.order(:assessment_date).last end def latest_cp_assessment - cp_assessments.first - end - - def to_s - name + cp_assessments.order(:assessment_date).last end def sector_benchmarks diff --git a/spec/models/company_spec.rb b/spec/models/company_spec.rb index 1735785c5..1c0b77245 100644 --- a/spec/models/company_spec.rb +++ b/spec/models/company_spec.rb @@ -60,4 +60,30 @@ subject.visibility_status = nil expect(subject).to have(2).errors_on(:visibility_status) end + + describe '#latest_cp_assessment' do + it 'returns last CP assessments with most recent assessment date' do + company = create(:company, cp_assessments: [ + create(:cp_assessment, assessment_date: '2012-05-01'), + create(:cp_assessment, assessment_date: '2019-05-01'), + create(:cp_assessment, assessment_date: '2013-05-01'), + create(:cp_assessment, assessment_date: '2018-05-01') + ]) + + expect(company.latest_cp_assessment.assessment_date.to_s).to eq('2019-05-01') + end + end + + describe '#latest_mq_assessment' do + it 'returns last MQ assessments with most recent assessment date' do + company = create(:company, mq_assessments: [ + create(:mq_assessment, assessment_date: '2012-05-01'), + create(:mq_assessment, assessment_date: '2019-05-01'), + create(:mq_assessment, assessment_date: '2013-05-01'), + create(:mq_assessment, assessment_date: '2018-05-01') + ]) + + expect(company.latest_mq_assessment.assessment_date.to_s).to eq('2019-05-01') + end + end end From 4e3eb5aedeea92bfb1e141bbf4ca1096c5cfd65e Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Tue, 24 Sep 2019 18:47:53 +0200 Subject: [PATCH 38/56] First version of "Carbon Performance: All sectors" chart with real data --- app/services/api/charts/sector.rb | 108 +++++++++++++++++++++------ app/views/tpi/sectors/index.html.erb | 2 +- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 1af285251..213057829 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -1,8 +1,6 @@ module Api module Charts class Sector - CP_SCENARIOS = %w[below_2 exact_2 paris not_aligned no_disclosure].freeze - def initialize(company_scope) @company_scope = company_scope end @@ -79,12 +77,7 @@ def companies_emissions_data # } # ] def group_by_cp_benchmark - CP_SCENARIOS.map do |cp_alignment| - { - name: get_alignment_label(cp_alignment), - data: companies_count_per_sector_cp_scenarios(cp_alignment) - } - end + prepare_chart_data(sectors_scenarios) end private @@ -112,22 +105,6 @@ def emissions_data_from_sector_benchmarks end end - def get_alignment_label(cp_alignment) - { - below_2: 'Below 2', - exact_2: '2 degrees', - paris: 'Paris', - not_aligned: 'Not aligned', - no_disclosure: 'No disclosure' - }[cp_alignment.to_sym] - end - - def companies_count_per_sector_cp_scenarios(_cp_alignment) - ::Sector.pluck(:id, :name).map do |_sector_id, sector_name| - [sector_name, rand(100)] - end - end - def company_emissions_series_options(company) { name: company.name, @@ -145,6 +122,89 @@ def companies_summary(companies) } end end + + # Returns scenarios with sectors and companies count: + # @example + # { + # "2 Degrees (High Efficiency)" => { + # "Aluminium" => 10, + # "Cement" => 27 + # }, + # "International Pledges" => { + # "Steel" => 80 + # } + # } + def sectors_scenarios + results = {} + + sectors_scenario_emissions.each do |sector, scenarios| + sector.companies.each do |company| + next if scenarios.empty? + + company_scenario = company_scenario(company, scenarios) + + results[company_scenario] ||= Hash.new(0) + results[company_scenario][sector.name] += 1 + end + end + + results + end + + # Returns emissions for all scenarios for all sectors + # @example + # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} + def sectors_scenario_emissions + sectors = {} + + ::Sector.all.each do |sector| + sectors[sector] = {} + sector.cp_benchmarks.each do |benchmark| + sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] + end + end + + sectors + end + + # Determine in which scenario is current company emission + def company_scenario(company, scenarios) + company_emission = company_emission(company) + + scenarios_with_greater_emission = scenarios.select do |_s, value| + value >= company_emission + end + + return scenarios.max.first if scenarios_with_greater_emission.empty? + + scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first + end + + # Returns company emission for current year or for the latest assessment + def company_emission(company) + emissions = company.cp_assessments.order(:assessment_date).last&.emissions + + return 0 if emissions.nil? or emissions&.empty? + + # Take emission for current year or for the last emission + emissions[current_year] or emissions.max_by { |year| year }[1] + end + + def current_year + @current_year ||= Time.new.year.to_s + end + + # Prepare data for chart based on data + def prepare_chart_data(results) + results.map do |scenario, companies| + data = companies.map do |company, value| + [company, value] + end + + {name: scenario, data: data} + end + end + end end end diff --git a/app/views/tpi/sectors/index.html.erb b/app/views/tpi/sectors/index.html.erb index 951eef837..3c12f81bb 100644 --- a/app/views/tpi/sectors/index.html.erb +++ b/app/views/tpi/sectors/index.html.erb @@ -27,7 +27,7 @@ Companies:
-

Carbon Performance: All sectors (random data)

+

Carbon Performance: All sectors

CP alignment with the Paris agreement benchmarks by sector and cluster (number and % of companies). Please note that this information is not available for all sectors.

From 5436087a011802c1668b29ed550933a2eb646a2c Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Tue, 24 Sep 2019 18:52:21 +0200 Subject: [PATCH 39/56] Fix rubocop error --- app/services/api/charts/sector.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 213057829..20148db61 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -204,7 +204,6 @@ def prepare_chart_data(results) {name: scenario, data: data} end end - end end end From 96adf8c7dc179f0338d194a7df0b72c004969279 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 10:13:08 +0200 Subject: [PATCH 40/56] Fix case when company does not have any emission reported --- app/services/api/charts/sector.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 20148db61..51ac47088 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -141,7 +141,11 @@ def sectors_scenarios sector.companies.each do |company| next if scenarios.empty? - company_scenario = company_scenario(company, scenarios) + company_emission = company_emission(company) + + next if company_emission.zero? + + company_scenario = company_scenario(company_emission, scenarios) results[company_scenario] ||= Hash.new(0) results[company_scenario][sector.name] += 1 @@ -168,9 +172,7 @@ def sectors_scenario_emissions end # Determine in which scenario is current company emission - def company_scenario(company, scenarios) - company_emission = company_emission(company) - + def company_scenario(company_emission, scenarios) scenarios_with_greater_emission = scenarios.select do |_s, value| value >= company_emission end From 473f8b627be6604fc46756f090fdcf9cc319a7f7 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 10:53:15 +0200 Subject: [PATCH 41/56] Move cp_benchmark chart to another file --- app/controllers/tpi/sectors_controller.rb | 2 +- app/services/api/charts/cp_benchmark.rb | 110 ++++++++++++++++++++++ app/services/api/charts/sector.rb | 103 -------------------- 3 files changed, 111 insertions(+), 104 deletions(-) create mode 100644 app/services/api/charts/cp_benchmark.rb diff --git a/app/controllers/tpi/sectors_controller.rb b/app/controllers/tpi/sectors_controller.rb index ff41ccdf2..1e4e608e6 100644 --- a/app/controllers/tpi/sectors_controller.rb +++ b/app/controllers/tpi/sectors_controller.rb @@ -38,7 +38,7 @@ def emissions_chart_data # Type: column chart # On pages: :index def benchmarks_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).group_by_cp_benchmark + data = ::Api::Charts::CPBenchmark.new.group_by_cp_benchmark render json: data.chart_json end diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb new file mode 100644 index 000000000..a8e7e87d9 --- /dev/null +++ b/app/services/api/charts/cp_benchmark.rb @@ -0,0 +1,110 @@ +module Api + module Charts + class CPBenchmark + # Returns Companies stats grouped by CP benchmark. + # + # Returns data in multiple series. + # + # @example + # [ + # { + # name: 'Below 2', + # data: [ ['Coal Mining', 52], ['Steel', 73] ] + # }, + # { + # name: 'Paris', + # data: [ ['Coal Mining', 65], ['Steel', 26] ] + # } + # ] + def group_by_cp_benchmark + prepare_chart_data(sectors_scenarios) + end + + private + + # Returns scenarios with sectors and companies count: + # @example + # { + # "2 Degrees (High Efficiency)" => { + # "Aluminium" => 10, + # "Cement" => 27 + # }, + # "International Pledges" => { + # "Steel" => 80 + # } + # } + def sectors_scenarios + results = {} + + sectors_scenario_emissions.each do |sector, scenarios| + sector.companies.each do |company| + next if scenarios.empty? + + company_emission = company_emission(company) + + next if company_emission.zero? + + company_scenario = company_scenario(company_emission, scenarios) + + results[company_scenario] ||= Hash.new(0) + results[company_scenario][sector.name] += 1 + end + end + + results + end + + # Returns emissions for all scenarios for all sectors + # @example + # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} + def sectors_scenario_emissions + sectors = {} + + ::Sector.all.each do |sector| + sectors[sector] = {} + sector.cp_benchmarks.each do |benchmark| + sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] + end + end + + sectors + end + + # Determine in which scenario is current company emission + def company_scenario(company_emission, scenarios) + scenarios_with_greater_emission = scenarios.select do |_s, value| + value >= company_emission + end + + return scenarios.max.first if scenarios_with_greater_emission.empty? + + scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first + end + + # Returns company emission for current year or for the latest assessment + def company_emission(company) + emissions = company.cp_assessments.order(:assessment_date).last&.emissions + + return 0 if emissions.nil? or emissions&.empty? + + # Take emission for current year or for the last emission + emissions[current_year] or emissions.max_by { |year| year }[1] + end + + def current_year + @current_year ||= Time.new.year.to_s + end + + # Prepare data for chart based on data + def prepare_chart_data(results) + results.map do |scenario, companies| + data = companies.map do |company, value| + [company, value] + end + + {name: scenario, data: data} + end + end + end + end +end diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 51ac47088..314369f11 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -61,25 +61,6 @@ def companies_emissions_data ].flatten end - # Returns Companies stats grouped by CP benchmark. - # - # Returns data in multiple series. - # - # @example - # [ - # { - # name: 'Below 2', - # data: [ ['Coal Mining', 52], ['Steel', 73] ] - # }, - # { - # name: 'Paris', - # data: [ ['Coal Mining', 65], ['Steel', 26] ] - # } - # ] - def group_by_cp_benchmark - prepare_chart_data(sectors_scenarios) - end - private def companies_grouped_by_latest_assessment_level @@ -122,90 +103,6 @@ def companies_summary(companies) } end end - - # Returns scenarios with sectors and companies count: - # @example - # { - # "2 Degrees (High Efficiency)" => { - # "Aluminium" => 10, - # "Cement" => 27 - # }, - # "International Pledges" => { - # "Steel" => 80 - # } - # } - def sectors_scenarios - results = {} - - sectors_scenario_emissions.each do |sector, scenarios| - sector.companies.each do |company| - next if scenarios.empty? - - company_emission = company_emission(company) - - next if company_emission.zero? - - company_scenario = company_scenario(company_emission, scenarios) - - results[company_scenario] ||= Hash.new(0) - results[company_scenario][sector.name] += 1 - end - end - - results - end - - # Returns emissions for all scenarios for all sectors - # @example - # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} - def sectors_scenario_emissions - sectors = {} - - ::Sector.all.each do |sector| - sectors[sector] = {} - sector.cp_benchmarks.each do |benchmark| - sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] - end - end - - sectors - end - - # Determine in which scenario is current company emission - def company_scenario(company_emission, scenarios) - scenarios_with_greater_emission = scenarios.select do |_s, value| - value >= company_emission - end - - return scenarios.max.first if scenarios_with_greater_emission.empty? - - scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first - end - - # Returns company emission for current year or for the latest assessment - def company_emission(company) - emissions = company.cp_assessments.order(:assessment_date).last&.emissions - - return 0 if emissions.nil? or emissions&.empty? - - # Take emission for current year or for the last emission - emissions[current_year] or emissions.max_by { |year| year }[1] - end - - def current_year - @current_year ||= Time.new.year.to_s - end - - # Prepare data for chart based on data - def prepare_chart_data(results) - results.map do |scenario, companies| - data = companies.map do |company, value| - [company, value] - end - - {name: scenario, data: data} - end - end end end end From 207554ead643daffc7b1992d0e4ed5975052b3d3 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 13:28:48 +0200 Subject: [PATCH 42/56] Unit tests for `Carbon Performance: All sectors` chart --- app/controllers/tpi/sectors_controller.rb | 2 +- app/services/api/charts/cp_benchmark.rb | 2 +- spec/services/api/charts/cp_benchmark_spec.rb | 82 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 spec/services/api/charts/cp_benchmark_spec.rb diff --git a/app/controllers/tpi/sectors_controller.rb b/app/controllers/tpi/sectors_controller.rb index 1e4e608e6..c54da3f28 100644 --- a/app/controllers/tpi/sectors_controller.rb +++ b/app/controllers/tpi/sectors_controller.rb @@ -38,7 +38,7 @@ def emissions_chart_data # Type: column chart # On pages: :index def benchmarks_chart_data - data = ::Api::Charts::CPBenchmark.new.group_by_cp_benchmark + data = ::Api::Charts::CPBenchmark.new.cp_performance render json: data.chart_json end diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index a8e7e87d9..98298d17b 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -16,7 +16,7 @@ class CPBenchmark # data: [ ['Coal Mining', 65], ['Steel', 26] ] # } # ] - def group_by_cp_benchmark + def cp_performance prepare_chart_data(sectors_scenarios) end diff --git a/spec/services/api/charts/cp_benchmark_spec.rb b/spec/services/api/charts/cp_benchmark_spec.rb new file mode 100644 index 000000000..d231e3e63 --- /dev/null +++ b/spec/services/api/charts/cp_benchmark_spec.rb @@ -0,0 +1,82 @@ +require 'rails_helper' + +RSpec.describe Api::Charts::CPBenchmark do + subject { described_class.new } + let!(:sector) { create(:sector) } + let!(:company) { create(:company, sector: sector) } + let(:current_year) { '2018' } + + before do + allow(subject) + .to receive(:current_year) + .and_return(current_year) + end + + describe '.cp_performance' do + context 'sector has no scenarios' do + it 'returns empty list' do + expect(subject.cp_performance).to eq([]) + end + + context 'sector has scenarios but company has no emissions' do + let!(:cp_benchmark) { create(:cp_benchmark, sector: sector) } + + it 'returns empty list' do + expect(subject.cp_performance).to eq([]) + end + end + + context 'company has emissions' do + let!(:scenario_1) { 'scenario_1' } + let!(:scenario_2) { 'scenario_2' } + before do + create( + :cp_benchmark, + scenario: scenario_1, + sector: sector, + emissions: {"2018": 124.0, "2017": 122.0} + ) + create( + :cp_benchmark, + scenario: scenario_2, + sector: sector, + emissions: {"2018": 110.0, "2017": 90.0} + ) + create( + :cp_assessment, + company: company, + assessment_date: '2019-02-01', + emissions: {"2017": 90.0, "2018": 90.0, "2019": 110.0} + ) + create( + :cp_assessment, + company: company, + assessment_date: '2019-01-01', + emissions: {"2017": 90.0, "2018": 120.0, "2019": 110.0} + ) + create( + :cp_assessment, + company: create(:company, sector: sector), + assessment_date: '2019-01-01', + emissions: {"2016": 90.0, "2017": 120.0} + ) + end + + it 'returns chart data' do + expect(subject.cp_performance).to eq( + [ + { + name: scenario_2, + data: [[sector.name, 1]] + }, + { + name: scenario_1, + data: [[sector.name, 1]] + } + ] + ) + end + end + end + end +end From 28b7fb620e793f2eda28e10e323260bb3888211c Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 15:56:40 +0200 Subject: [PATCH 43/56] Documentation for `Carbon Performance: All sectors` --- app/services/api/charts/cp_benchmark.rb | 65 ++++++++++++++----------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 98298d17b..16e18c249 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -1,10 +1,8 @@ module Api module Charts class CPBenchmark - # Returns Companies stats grouped by CP benchmark. - # - # Returns data in multiple series. - # + # Calculate companies stats grouped by CP benchmark in multiple series. + # @return [Array chart data # @example # [ # { @@ -17,12 +15,26 @@ class CPBenchmark # } # ] def cp_performance - prepare_chart_data(sectors_scenarios) + prepare_chart_data(sectors_cp_performance) end private - # Returns scenarios with sectors and companies count: + # Prepare data for chart based on sectors cp performance data + # @param results[Array] list with all scenarios with emissions + # @return [Array] list with data for chart + def prepare_chart_data(results) + results.map do |scenario, companies| + data = companies.map do |company, value| + [company, value] + end + + {name: scenario, data: data} + end + end + + # Scenarios with sectors and companies count: + # @return [Hash] # @example # { # "2 Degrees (High Efficiency)" => { @@ -33,7 +45,7 @@ def cp_performance # "Steel" => 80 # } # } - def sectors_scenarios + def sectors_cp_performance results = {} sectors_scenario_emissions.each do |sector, scenarios| @@ -54,7 +66,8 @@ def sectors_scenarios results end - # Returns emissions for all scenarios for all sectors + # Calculate emissions for all scenarios for all sectors + # @return [Hash] # @example # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} def sectors_scenario_emissions @@ -70,7 +83,21 @@ def sectors_scenario_emissions sectors end + # Get company emission for current year or for the latest assessment + # @param company [Company] + # @return [Float] + def company_emission(company) + emissions = company.cp_assessments.order(:assessment_date).last&.emissions + + return 0 if emissions.nil? or emissions&.empty? + + # Take emission for current year or for the last emission + emissions[current_year] or emissions.max_by { |year| year }[1] + end + # Determine in which scenario is current company emission + # @param company_emission [Float] + # @param scenarios [Hash] def company_scenario(company_emission, scenarios) scenarios_with_greater_emission = scenarios.select do |_s, value| value >= company_emission @@ -81,30 +108,10 @@ def company_scenario(company_emission, scenarios) scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first end - # Returns company emission for current year or for the latest assessment - def company_emission(company) - emissions = company.cp_assessments.order(:assessment_date).last&.emissions - - return 0 if emissions.nil? or emissions&.empty? - - # Take emission for current year or for the last emission - emissions[current_year] or emissions.max_by { |year| year }[1] - end - + # @return [String] string with current year def current_year @current_year ||= Time.new.year.to_s end - - # Prepare data for chart based on data - def prepare_chart_data(results) - results.map do |scenario, companies| - data = companies.map do |company, value| - [company, value] - end - - {name: scenario, data: data} - end - end end end end From 28c043e3df82ecc896e0c494904c72cf6eb94dc4 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 16:13:25 +0200 Subject: [PATCH 44/56] Refactoring for `Carbon Performance: All sectors` --- app/services/api/charts/cp_benchmark.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 16e18c249..99f72c2c4 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -33,7 +33,7 @@ def prepare_chart_data(results) end end - # Scenarios with sectors and companies count: + # Scenarios with sectors and companies count # @return [Hash] # @example # { @@ -54,7 +54,7 @@ def sectors_cp_performance company_emission = company_emission(company) - next if company_emission.zero? + next unless company_emission company_scenario = company_scenario(company_emission, scenarios) @@ -66,7 +66,7 @@ def sectors_cp_performance results end - # Calculate emissions for all scenarios for all sectors + # Get emissions for all scenarios for all sectors # @return [Hash] # @example # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} @@ -89,10 +89,10 @@ def sectors_scenario_emissions def company_emission(company) emissions = company.cp_assessments.order(:assessment_date).last&.emissions - return 0 if emissions.nil? or emissions&.empty? + return if emissions.blank? # Take emission for current year or for the last emission - emissions[current_year] or emissions.max_by { |year| year }[1] + emissions[current_year] or emissions[emissions.keys.max] end # Determine in which scenario is current company emission From f3e2dddbe35484d2da69251b16dbb5c47397947a Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 17:24:56 +0200 Subject: [PATCH 45/56] Unit tests for company.rb charts --- app/services/api/charts/company.rb | 2 +- spec/services/api/charts/company_spec.rb | 49 ++++++++++++++++++++++++ spec/spec_helper.rb | 4 +- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 spec/services/api/charts/company_spec.rb diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index 5d1cc639a..f9b227cdb 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -76,7 +76,7 @@ def sector_average_emissions years = (all_years.min..last_reported_year).map.to_a years - .map { |year| [year, sector_average_emission_for_year(year)] } + .map { |year| [year.to_s, sector_average_emission_for_year(year)] } .to_h end diff --git a/spec/services/api/charts/company_spec.rb b/spec/services/api/charts/company_spec.rb new file mode 100644 index 000000000..0f13e07b0 --- /dev/null +++ b/spec/services/api/charts/company_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +RSpec.describe Api::Charts::Company do + let(:sector) { create(:sector) } + let(:company) { create(:company, sector: sector) } + let(:another_company_from_sector) { create(:company, sector: sector) } + + subject { described_class.new(company) } + + describe '.emissions_data' do + before do + create( + :cp_assessment, + company: company, + emissions: {'2017' => 90.0, '2018' => 90.0, '2019' => 110.0} + ) + create( + :cp_assessment, + company: another_company_from_sector, + emissions: {'2017' => 40.0, '2018' => 50.0} + ) + create( + :cp_benchmark, + scenario: 'scenario', + sector: sector, + emissions: {'2018' => 124.0, '2017' => 122.0} + ) + end + + it 'returns all series data' do + expect(subject.emissions_data).to eq [ + { + name: company.name, + data: {'2017' => 90.0, '2018' => 90.0, '2019' => 110.0} + }, + { + name: "#{sector.name} sector mean", + data: {'2017' => 65.0, '2018' => 70.0, '2019' => 110.0} + }, + { + type: 'area', + fillOpacity: 0.1, + name: 'scenario', + data: {'2017' => 122.0, '2018' => 124.0} + } + ] + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 25a7fad5e..cad55850b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ require 'simplecov' -SimpleCov.start +SimpleCov.start do + add_filter '/spec/' +end # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. From 518aced83ee710d29f5a3d5a178ec3cc44c6f11d Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Wed, 25 Sep 2019 21:34:03 +0200 Subject: [PATCH 46/56] Unit tests for sector.rb charts --- spec/services/api/charts/sector_spec.rb | 79 +++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 spec/services/api/charts/sector_spec.rb diff --git a/spec/services/api/charts/sector_spec.rb b/spec/services/api/charts/sector_spec.rb new file mode 100644 index 000000000..71d58260d --- /dev/null +++ b/spec/services/api/charts/sector_spec.rb @@ -0,0 +1,79 @@ +require 'rails_helper' + +RSpec.describe Api::Charts::Sector do + let(:sector) { create(:sector) } + let(:company) { create(:company, sector: sector) } + let(:company2) { create(:company, sector: sector) } + + subject { described_class.new(Company) } + + before do + create( + :mq_assessment, + assessment_date: '2019-02-01', + level: 1, + company: company + ) + create( + :mq_assessment, + assessment_date: '2019-01-01', + level: 2, + company: company + ) + create( + :mq_assessment, + company: company2 + ) + end + + describe '.companies_summaries' do + it 'returns Companies summaries grouped by their latest' do + expect(subject.companies_summaries).to eq( + [ + ['1', [{id: company2.id, name: company2.name, status: 'new'}]], + ['2', [{id: company.id, name: company.name, status: 'down'}]] + ] + ) + end + end + + describe '.companies_count' do + it 'returns companies summaries' do + expect(subject.companies_count).to eq('1' => 1, '2' => 1) + end + end + + describe '.companies_emissions_data' do + before do + create( + :cp_assessment, + company: company, + assessment_date: '2019-01-01', + emissions: {'2017': 90.0, '2018': 120.0, '2019': 110.0} + ) + create( + :cp_assessment, + company: company2, + assessment_date: '2019-01-01', + emissions: {'2017': 190.0, '2018': 220.0, '2019': 90.0} + ) + end + + it 'returns companies emissions' do + expect(subject.companies_emissions_data).to eq( + [ + { + data: {'2017' => 90.0, '2018' => 120.0, '2019' => 110.0}, + lineWidth: 4, + name: company.name + }, + { + data: {'2017' => 190.0, '2018' => 220.0, '2019' => 90.0}, + lineWidth: 4, + name: company2.name + } + ] + ) + end + end +end From 69937c70bbff062fb64ff9d51abefefa176cd069 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Thu, 26 Sep 2019 23:39:10 +0200 Subject: [PATCH 47/56] One more corner case with more unit tests --- app/services/api/charts/cp_benchmark.rb | 4 +++- spec/services/api/charts/cp_benchmark_spec.rb | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 99f72c2c4..7f28dd5e2 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -103,7 +103,9 @@ def company_scenario(company_emission, scenarios) value >= company_emission end - return scenarios.max.first if scenarios_with_greater_emission.empty? + if scenarios_with_greater_emission.empty? + return scenarios.max_by { |_s, value| value }.first + end scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first end diff --git a/spec/services/api/charts/cp_benchmark_spec.rb b/spec/services/api/charts/cp_benchmark_spec.rb index d231e3e63..97fa905c3 100644 --- a/spec/services/api/charts/cp_benchmark_spec.rb +++ b/spec/services/api/charts/cp_benchmark_spec.rb @@ -42,24 +42,41 @@ sector: sector, emissions: {"2018": 110.0, "2017": 90.0} ) + # Company in scenario_2 (2018: 90 < 110) create( :cp_assessment, company: company, assessment_date: '2019-02-01', emissions: {"2017": 90.0, "2018": 90.0, "2019": 110.0} ) + # Old assessment -> not included in chart create( :cp_assessment, company: company, assessment_date: '2019-01-01', emissions: {"2017": 90.0, "2018": 120.0, "2019": 110.0} ) + # Company in scenario_1 (2017: 120 > 110) create( :cp_assessment, company: create(:company, sector: sector), assessment_date: '2019-01-01', emissions: {"2016": 90.0, "2017": 120.0} ) + # Company in scenario_2 (2017: 110 == 110 for scenario) + create( + :cp_assessment, + company: create(:company, sector: sector), + assessment_date: '2019-01-01', + emissions: {"2016": 90.0, "2017": 110} + ) + # Company in scenario_1 (2017: 125 > 124) + create( + :cp_assessment, + company: create(:company, sector: sector), + assessment_date: '2019-01-01', + emissions: {"2016": 90.0, "2017": 125.0} + ) end it 'returns chart data' do @@ -67,11 +84,11 @@ [ { name: scenario_2, - data: [[sector.name, 1]] + data: [[sector.name, 2]] }, { name: scenario_1, - data: [[sector.name, 1]] + data: [[sector.name, 2]] } ] ) From 2bbddf8cc16d658a2130ae6115879fbfdb577981 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Thu, 26 Sep 2019 23:45:33 +0200 Subject: [PATCH 48/56] Fix Rubocop error --- app/services/api/charts/cp_benchmark.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 7f28dd5e2..8485dd2d6 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -103,9 +103,7 @@ def company_scenario(company_emission, scenarios) value >= company_emission end - if scenarios_with_greater_emission.empty? - return scenarios.max_by { |_s, value| value }.first - end + return scenarios.max_by { |_s, value| value }.first if scenarios_with_greater_emission.empty? scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first end From 1b2722ee7cf837ea75a8699036c3d87d95062403 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Fri, 27 Sep 2019 12:37:44 +0200 Subject: [PATCH 49/56] Get CP Benchmarks from last release --- app/services/api/charts/cp_benchmark.rb | 13 ++++++++++++- spec/services/api/charts/cp_benchmark_spec.rb | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 8485dd2d6..1f5ce3417 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -75,7 +75,7 @@ def sectors_scenario_emissions ::Sector.all.each do |sector| sectors[sector] = {} - sector.cp_benchmarks.each do |benchmark| + get_last_cp_benchmarks(sector).each do |benchmark| sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] end end @@ -108,6 +108,17 @@ def company_scenario(company_emission, scenarios) scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first end + # Get list of last CPBenchmarks for sector + # @param sector [Sector] + # @return [Array] + def get_last_cp_benchmarks(sector) + cp_benchmarks = sector.cp_benchmarks + + return [] if cp_benchmarks.empty? + + cp_benchmarks.group_by(&:release_date).max[1] + end + # @return [String] string with current year def current_year @current_year ||= Time.new.year.to_s diff --git a/spec/services/api/charts/cp_benchmark_spec.rb b/spec/services/api/charts/cp_benchmark_spec.rb index 97fa905c3..0214b3fb1 100644 --- a/spec/services/api/charts/cp_benchmark_spec.rb +++ b/spec/services/api/charts/cp_benchmark_spec.rb @@ -29,19 +29,30 @@ context 'company has emissions' do let!(:scenario_1) { 'scenario_1' } let!(:scenario_2) { 'scenario_2' } + let!(:scenario_3) { 'scenario_3' } before do create( :cp_benchmark, scenario: scenario_1, sector: sector, + release_date: '2019-01-01', emissions: {"2018": 124.0, "2017": 122.0} ) create( :cp_benchmark, scenario: scenario_2, sector: sector, + release_date: '2019-01-01', emissions: {"2018": 110.0, "2017": 90.0} ) + # CP benchmark in old release + create( + :cp_benchmark, + scenario: scenario_3, + sector: sector, + release_date: '2018-01-01', + emissions: {"2018": 100.0, "2017": 90.0} + ) # Company in scenario_2 (2018: 90 < 110) create( :cp_assessment, From 8db1d0ac6148b6ee1dfbfdbfe4648c55ea875371 Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Fri, 27 Sep 2019 13:04:16 +0200 Subject: [PATCH 50/56] Fix tests after rebase --- spec/services/api/charts/sector_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/services/api/charts/sector_spec.rb b/spec/services/api/charts/sector_spec.rb index 71d58260d..40864752e 100644 --- a/spec/services/api/charts/sector_spec.rb +++ b/spec/services/api/charts/sector_spec.rb @@ -29,9 +29,9 @@ describe '.companies_summaries' do it 'returns Companies summaries grouped by their latest' do expect(subject.companies_summaries).to eq( - [ - ['1', [{id: company2.id, name: company2.name, status: 'new'}]], - ['2', [{id: company.id, name: company.name, status: 'down'}]] + "1" => [ + {id: company.id, name: company.name, status: 'down'}, + {id: company2.id, name: company2.name, status: 'new'} ] ) end @@ -39,7 +39,7 @@ describe '.companies_count' do it 'returns companies summaries' do - expect(subject.companies_count).to eq('1' => 1, '2' => 1) + expect(subject.companies_count).to eq('1' => 2) end end From e5b466ea85e4adc8bfaa8018392efb594fb3517f Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Fri, 27 Sep 2019 13:08:14 +0200 Subject: [PATCH 51/56] Fix rubocop --- spec/services/api/charts/sector_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/api/charts/sector_spec.rb b/spec/services/api/charts/sector_spec.rb index 40864752e..873c2acb6 100644 --- a/spec/services/api/charts/sector_spec.rb +++ b/spec/services/api/charts/sector_spec.rb @@ -29,7 +29,7 @@ describe '.companies_summaries' do it 'returns Companies summaries grouped by their latest' do expect(subject.companies_summaries).to eq( - "1" => [ + '1' => [ {id: company.id, name: company.name, status: 'down'}, {id: company2.id, name: company2.name, status: 'new'} ] From e263e6f8ac2fe3a13953dcb0ed600250687e0d8f Mon Sep 17 00:00:00 2001 From: Adam Gwozdowski Date: Fri, 27 Sep 2019 13:55:13 +0200 Subject: [PATCH 52/56] Eager loading and refactoring --- app/services/api/charts/cp_benchmark.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 1f5ce3417..e0b6dc08e 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -50,8 +50,6 @@ def sectors_cp_performance sectors_scenario_emissions.each do |sector, scenarios| sector.companies.each do |company| - next if scenarios.empty? - company_emission = company_emission(company) next unless company_emission @@ -73,7 +71,7 @@ def sectors_cp_performance def sectors_scenario_emissions sectors = {} - ::Sector.all.each do |sector| + ::Sector.all.includes(:cp_benchmarks, companies: [:cp_assessments]).each do |sector| sectors[sector] = {} get_last_cp_benchmarks(sector).each do |benchmark| sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] @@ -87,7 +85,7 @@ def sectors_scenario_emissions # @param company [Company] # @return [Float] def company_emission(company) - emissions = company.cp_assessments.order(:assessment_date).last&.emissions + emissions = company.latest_cp_assessment&.emissions return if emissions.blank? From 80eb81fd05baddb381d9d3bfac1adda1869cf4e9 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 27 Sep 2019 16:01:50 +0200 Subject: [PATCH 53/56] update mq_assessment.questions usage --- app/models/mq/assessment.rb | 2 +- app/services/api/presenters/company.rb | 2 +- app/views/tpi/companies/show.html.erb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models/mq/assessment.rb b/app/models/mq/assessment.rb index 2d1aca3c0..f6f1fbd9b 100644 --- a/app/models/mq/assessment.rb +++ b/app/models/mq/assessment.rb @@ -63,7 +63,7 @@ def find_answer_by_key(key) end def questions_by_level - questions.group_by { |q| q['level'] } + questions.group_by(&:level) end end end diff --git a/app/services/api/presenters/company.rb b/app/services/api/presenters/company.rb index 541c6e1c0..954f9ca42 100644 --- a/app/services/api/presenters/company.rb +++ b/app/services/api/presenters/company.rb @@ -22,7 +22,7 @@ def company_details isin: @company.isin, sedol: 60, ca100: @company.ca100 ? 'Yes' : 'No', - latest_assessment: @company.latest_mq_assessment.questions_by_level, + latest_assessment_questions: @company.latest_mq_assessment.questions_by_level, levels_descriptions: SECTORS_LEVELS_DESC } end diff --git a/app/views/tpi/companies/show.html.erb b/app/views/tpi/companies/show.html.erb index 567f631f4..ae021f782 100644 --- a/app/views/tpi/companies/show.html.erb +++ b/app/views/tpi/companies/show.html.erb @@ -47,14 +47,14 @@

-<%- @company_details[:latest_assessment].each do |level, assessments| %> +<%- @company_details[:latest_assessment_questions].each do |level, questions| %>

Level <%= level %>: <%= @company_details[:levels_descriptions][level.to_s] || 'Answers' %>

- <%- assessments.each do |a| %> + <%- questions.each do |question| %>
-
(<%= a['answer'] %>)
-
<%= a['question'] %>
+
(<%= question.answer %>)
+
<%= question.question %>
<%- end %>
From 21a86c011c791842fc74a7cf819fc1a9924c53f7 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 27 Sep 2019 16:30:52 +0200 Subject: [PATCH 54/56] revert changes --- app/admin/companies.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/admin/companies.rb b/app/admin/companies.rb index 7892bb48f..c6e151439 100644 --- a/app/admin/companies.rb +++ b/app/admin/companies.rb @@ -56,7 +56,7 @@ 'No Management Quality Assessments for this company yet' end else - resource.mq_assessments.decorate.map do |a| + resource.mq_assessments.latest_first.decorate.map do |a| panel a.title, class: 'mq_assessment' do attributes_table_for a do row :level, &:level_tag @@ -82,7 +82,7 @@ 'No Carbon Performance Assessments for this company yet' end else - resource.cp_assessments.decorate.map do |a| + resource.cp_assessments.latest_first.decorate.map do |a| div class: 'panel benchmark' do attributes_table_for a do row :publication_date From 81bf1bbcf6bf2d6f998a10b44f2c7c837c48d394 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Fri, 27 Sep 2019 16:41:26 +0200 Subject: [PATCH 55/56] some renames and docs update --- app/controllers/tpi/sectors_controller.rb | 6 +-- app/services/api/charts/company.rb | 9 +--- app/services/api/charts/cp_benchmark.rb | 11 ++++- app/services/api/charts/sector.rb | 49 ++++++++++--------- spec/services/api/charts/cp_benchmark_spec.rb | 6 +-- spec/services/api/charts/sector_spec.rb | 8 +-- 6 files changed, 46 insertions(+), 43 deletions(-) diff --git a/app/controllers/tpi/sectors_controller.rb b/app/controllers/tpi/sectors_controller.rb index c54da3f28..31f553e30 100644 --- a/app/controllers/tpi/sectors_controller.rb +++ b/app/controllers/tpi/sectors_controller.rb @@ -8,7 +8,7 @@ def index def show @sector = Sector.find(params[:id]) - @companies_by_levels = ::Api::Charts::Sector.new(companies_scope(params)).companies_summaries + @companies_by_levels = ::Api::Charts::Sector.new(companies_scope(params)).companies_summaries_by_level end # Chart data endpoints @@ -18,7 +18,7 @@ def show # Type: pie chart # On pages: :index, :show def levels_chart_data - data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count + data = ::Api::Charts::Sector.new(companies_scope(params)).companies_count_by_level render json: data.chart_json end @@ -38,7 +38,7 @@ def emissions_chart_data # Type: column chart # On pages: :index def benchmarks_chart_data - data = ::Api::Charts::CPBenchmark.new.cp_performance + data = ::Api::Charts::CPBenchmark.new.cp_performance_data render json: data.chart_json end diff --git a/app/services/api/charts/company.rb b/app/services/api/charts/company.rb index f9b227cdb..ea5d8ee51 100644 --- a/app/services/api/charts/company.rb +++ b/app/services/api/charts/company.rb @@ -10,6 +10,7 @@ def initialize(company) # - company's sector average emissions # - company's sector CP benchmarks (scenarios) # + # @return [Array] # @example # [ # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, @@ -44,14 +45,6 @@ def emissions_data_from_sector } end - # if we have benchmarks available for - # - 04.2017 - # .. assessment date is 06.2017 => we take 04.2017 benchmarks - # - 05.2018 - # .. assessment date is 06.2018 => we take 05.2018 benchmarks - # - # for the chart sector benchmarks should be only - # for the last date before the CP::Assessment date def emissions_data_from_sector_benchmarks @company.sector.cp_benchmarks.map do |benchmark| { diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index e0b6dc08e..155485b4b 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -2,6 +2,7 @@ module Api module Charts class CPBenchmark # Calculate companies stats grouped by CP benchmark in multiple series. + # # @return [Array chart data # @example # [ @@ -14,13 +15,14 @@ class CPBenchmark # data: [ ['Coal Mining', 65], ['Steel', 26] ] # } # ] - def cp_performance + def cp_performance_data prepare_chart_data(sectors_cp_performance) end private # Prepare data for chart based on sectors cp performance data + # # @param results[Array] list with all scenarios with emissions # @return [Array] list with data for chart def prepare_chart_data(results) @@ -34,6 +36,7 @@ def prepare_chart_data(results) end # Scenarios with sectors and companies count + # # @return [Hash] # @example # { @@ -41,7 +44,7 @@ def prepare_chart_data(results) # "Aluminium" => 10, # "Cement" => 27 # }, - # "International Pledges" => { + # "International Pledges" => { # "Steel" => 80 # } # } @@ -65,6 +68,7 @@ def sectors_cp_performance end # Get emissions for all scenarios for all sectors + # # @return [Hash] # @example # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} @@ -82,6 +86,7 @@ def sectors_scenario_emissions end # Get company emission for current year or for the latest assessment + # # @param company [Company] # @return [Float] def company_emission(company) @@ -94,6 +99,7 @@ def company_emission(company) end # Determine in which scenario is current company emission + # # @param company_emission [Float] # @param scenarios [Hash] def company_scenario(company_emission, scenarios) @@ -107,6 +113,7 @@ def company_scenario(company_emission, scenarios) end # Get list of last CPBenchmarks for sector + # # @param sector [Sector] # @return [Array] def get_last_cp_benchmarks(sector) diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index 314369f11..b9d8dff8a 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -5,53 +5,54 @@ def initialize(company_scope) @company_scope = company_scope end - # Returns Companies summaries grouped by their latest MQ assessments Sector's levels. + # Returns Companies summaries (name, status) grouped by their latest MQ assessments levels. # # Returns data in multiple series. # + # @return [Hash] # @example - # [ - # ['1', - # [ - # { name: 'Air China', status: 'new' }, - # { name: 'China Southern', status: 'new' } - # ] - # ], - # ['3', - # [ - # { name: 'Alaska Air', status: 'up' }, - # { name: 'IAG', status: 'down' } - # ] - # ] - # ] - def companies_summaries + # { + # '1' => [ + # { name: 'Air China', status: 'new' }, + # { name: 'China Southern', status: 'new' } + # ] + # '3' => [ + # { name: 'Alaska Air', status: 'up' }, + # { name: 'IAG', status: 'down' } + # ] + # } + def companies_summaries_by_level companies_grouped_by_latest_assessment_level .map { |level, companies| [level, companies_summary(companies)] } .sort.to_h end - # Returns latest "MQ assessment Sector's levels" mapped to number of Companies in given level + # Returns Companies count grouped by their latest MQ assessment levels. # # Returns single series of data. # + # @return [Hash] # @example # { '0' => 13, '1' => 63, '2' => 61, '3' => 71, '4' => 63, '4STAR' => 6} # - def companies_count + def companies_count_by_level companies_grouped_by_latest_assessment_level .map { |level, companies| [level, companies.size] } .sort.to_h end - # Returns companies emissions + # Returns companies emissions, which includes: + # - companies emissions + # - current Sector "scenario" emissions # # Returns data in multiple series. # + # @return [Array] # @example # [ - # { name: 'WizzAir', data: {} }, - # { name: 'Air China', data: {'2014' => 111.0, '2015' => 112.0 } }, - # { name: 'China Southern', data: {'2014' => 114.0, '2015' => 112.0 } } + # { name: 'WizzAir', data: { '2014' => 111.0, '2015' => 112.0 } }, + # { name: 'Air China', data: { .. } }, + # { name: '2 Degrees (High Efficiency)', data: { .. } } # ] # def companies_emissions_data @@ -76,6 +77,8 @@ def emissions_data_from_companies end def emissions_data_from_sector_benchmarks + # TODO: take last released benchmarks only + # (similar as in Api::Charts::CPBenchmark#get_last_cp_benchmarks) @company_scope.first.sector_benchmarks.map do |benchmark| { type: 'area', @@ -89,7 +92,7 @@ def emissions_data_from_sector_benchmarks def company_emissions_series_options(company) { name: company.name, - data: company.latest_cp_assessment.emissions, + data: company.latest_cp_assessment&.emissions, lineWidth: 4 } end diff --git a/spec/services/api/charts/cp_benchmark_spec.rb b/spec/services/api/charts/cp_benchmark_spec.rb index 0214b3fb1..b4b8060e6 100644 --- a/spec/services/api/charts/cp_benchmark_spec.rb +++ b/spec/services/api/charts/cp_benchmark_spec.rb @@ -15,14 +15,14 @@ describe '.cp_performance' do context 'sector has no scenarios' do it 'returns empty list' do - expect(subject.cp_performance).to eq([]) + expect(subject.cp_performance_data).to eq([]) end context 'sector has scenarios but company has no emissions' do let!(:cp_benchmark) { create(:cp_benchmark, sector: sector) } it 'returns empty list' do - expect(subject.cp_performance).to eq([]) + expect(subject.cp_performance_data).to eq([]) end end @@ -91,7 +91,7 @@ end it 'returns chart data' do - expect(subject.cp_performance).to eq( + expect(subject.cp_performance_data).to eq( [ { name: scenario_2, diff --git a/spec/services/api/charts/sector_spec.rb b/spec/services/api/charts/sector_spec.rb index 873c2acb6..d160ef309 100644 --- a/spec/services/api/charts/sector_spec.rb +++ b/spec/services/api/charts/sector_spec.rb @@ -27,8 +27,8 @@ end describe '.companies_summaries' do - it 'returns Companies summaries grouped by their latest' do - expect(subject.companies_summaries).to eq( + it 'returns Companies summaries grouped by their level' do + expect(subject.companies_summaries_by_level).to eq( '1' => [ {id: company.id, name: company.name, status: 'down'}, {id: company2.id, name: company2.name, status: 'new'} @@ -38,8 +38,8 @@ end describe '.companies_count' do - it 'returns companies summaries' do - expect(subject.companies_count).to eq('1' => 2) + it 'returns companies count grouped by their level' do + expect(subject.companies_count_by_level).to eq('1' => 2) end end From 08e932bff4eb5b56c2674d83cabb46355907afa9 Mon Sep 17 00:00:00 2001 From: Marek Kowalcze Date: Mon, 30 Sep 2019 15:18:17 +0200 Subject: [PATCH 56/56] (chart-api) fix for fetching sector benchmarks + clean up (#75) * extract Sector#latest_released_benchmarks and Company#latest_sector_* helpers * improve docs * fast return * udpate comment --- app/models/company.rb | 33 +++++++++++++- app/models/sector.rb | 4 ++ app/services/api/charts/company.rb | 32 +++++++++----- app/services/api/charts/cp_benchmark.rb | 27 ++++------- app/services/api/charts/sector.rb | 16 +++---- spec/models/company_spec.rb | 59 +++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 39 deletions(-) diff --git a/app/models/company.rb b/app/models/company.rb index b810bdd7b..c29f9c2a6 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -55,7 +55,36 @@ def latest_cp_assessment cp_assessments.order(:assessment_date).last end - def sector_benchmarks - sector.cp_benchmarks + def latest_sector_benchmarks + sector.latest_released_benchmarks + end + + # Returns sector CP benchmarks: + # - from the last date before latest CP::Assessment date + # (if company latest CP::Assessment was after at least one benchmark) + # - from latest benchmarks otherwise + # + # @return [AssociationRelation [# 111.0, '2015' => 112.0 } }, + # { name: 'Air China', data: { '2014' => 111.0, '2015' => 112.0 } }, # # { name: 'Airlines sector mean', data: { ... } }, # @@ -34,7 +34,7 @@ def emissions_data def emissions_data_from_company { name: @company.name, - data: @company.latest_cp_assessment.emissions + data: @company.latest_cp_assessment&.emissions } end @@ -46,7 +46,7 @@ def emissions_data_from_sector end def emissions_data_from_sector_benchmarks - @company.sector.cp_benchmarks.map do |benchmark| + @company.latest_sector_benchmarks_before_last_assessment.map do |benchmark| { type: 'area', fillOpacity: 0.1, @@ -63,17 +63,12 @@ def emissions_data_from_sector_benchmarks # @example {'2014' => 111.0, '2015' => 112.0 } # def sector_average_emissions - all_years = sector_all_emissions.map(&:keys).flatten.map(&:to_i).uniq - - last_reported_year = Time.new.year - years = (all_years.min..last_reported_year).map.to_a - - years + years_with_reported_emissions .map { |year| [year.to_s, sector_average_emission_for_year(year)] } .to_h end - # Returns average emission from all Companies from single year + # @return [Float] average emission from all Companies from single year def sector_average_emission_for_year(year) company_emissions = sector_all_emissions .map { |emissions| emissions[year.to_s] } @@ -82,7 +77,22 @@ def sector_average_emission_for_year(year) (company_emissions.sum / company_emissions.count).round(2) end - # Returns array of emissions ({ year => value }) from all Companies from current Sector + # @return [Array] of years for which emissions was reported + def years_with_reported_emissions + last_reported_year = Time.new.year + + (sector_all_emission_years.min..last_reported_year).map.to_a + end + + # @return [Array] unique array of years as numbers + def sector_all_emission_years + sector_all_emissions + .flat_map(&:keys) + .map(&:to_i) + .uniq + end + + # @return [Array] list of { year => value } pairs from all Companies from current Sector def sector_all_emissions @sector_all_emissions ||= @company.sector .companies diff --git a/app/services/api/charts/cp_benchmark.rb b/app/services/api/charts/cp_benchmark.rb index 155485b4b..b1d07e476 100644 --- a/app/services/api/charts/cp_benchmark.rb +++ b/app/services/api/charts/cp_benchmark.rb @@ -3,7 +3,7 @@ module Charts class CPBenchmark # Calculate companies stats grouped by CP benchmark in multiple series. # - # @return [Array chart data + # @return [Array] chart data # @example # [ # { @@ -73,16 +73,12 @@ def sectors_cp_performance # @example # {"Airlines": {"Below 2 Degrees": 110, "2 Degrees": 101}} def sectors_scenario_emissions - sectors = {} - - ::Sector.all.includes(:cp_benchmarks, companies: [:cp_assessments]).each do |sector| - sectors[sector] = {} - get_last_cp_benchmarks(sector).each do |benchmark| - sectors[sector][benchmark.scenario] = benchmark.emissions[current_year] + all_sectors.each_with_object({}) do |sector, sectors_hash| + sectors_hash[sector] = {} + sector.latest_released_benchmarks.each do |benchmark| + sectors_hash[sector][benchmark.scenario] = benchmark.emissions[current_year] end end - - sectors end # Get company emission for current year or for the latest assessment @@ -112,16 +108,11 @@ def company_scenario(company_emission, scenarios) scenarios_with_greater_emission.min_by { |_s, value| value - company_emission }.first end - # Get list of last CPBenchmarks for sector + # Get all Sectors # - # @param sector [Sector] - # @return [Array] - def get_last_cp_benchmarks(sector) - cp_benchmarks = sector.cp_benchmarks - - return [] if cp_benchmarks.empty? - - cp_benchmarks.group_by(&:release_date).max[1] + # @return [Sector::ActiveRecord_Relation] + def all_sectors + ::Sector.includes(:cp_benchmarks, companies: [:cp_assessments]) end # @return [String] string with current year diff --git a/app/services/api/charts/sector.rb b/app/services/api/charts/sector.rb index b9d8dff8a..e6ceb5763 100644 --- a/app/services/api/charts/sector.rb +++ b/app/services/api/charts/sector.rb @@ -43,16 +43,18 @@ def companies_count_by_level # Returns companies emissions, which includes: # - companies emissions - # - current Sector "scenario" emissions + # - current Sector "scenarios" emissions # # Returns data in multiple series. # # @return [Array] # @example # [ - # { name: 'WizzAir', data: { '2014' => 111.0, '2015' => 112.0 } }, + # { name: 'WizzAir', data: { '2014' => 111.0, '2015' => 112.0 } }, # { name: 'Air China', data: { .. } }, - # { name: '2 Degrees (High Efficiency)', data: { .. } } + # .. + # + # { name: '2 Degrees', data: { .. } } # ] # def companies_emissions_data @@ -73,13 +75,11 @@ def companies_grouped_by_latest_assessment_level def emissions_data_from_companies @company_scope .includes(:mq_assessments, :cp_assessments) - .map { |company| company_emissions_series_options(company) } + .map { |company| emissions_data_from_company(company) } end def emissions_data_from_sector_benchmarks - # TODO: take last released benchmarks only - # (similar as in Api::Charts::CPBenchmark#get_last_cp_benchmarks) - @company_scope.first.sector_benchmarks.map do |benchmark| + @company_scope.first.latest_sector_benchmarks.map do |benchmark| { type: 'area', fillOpacity: 0.1, @@ -89,7 +89,7 @@ def emissions_data_from_sector_benchmarks end end - def company_emissions_series_options(company) + def emissions_data_from_company(company) { name: company.name, data: company.latest_cp_assessment&.emissions, diff --git a/spec/models/company_spec.rb b/spec/models/company_spec.rb index 1c0b77245..98c49c5f8 100644 --- a/spec/models/company_spec.rb +++ b/spec/models/company_spec.rb @@ -86,4 +86,63 @@ expect(company.latest_mq_assessment.assessment_date.to_s).to eq('2019-05-01') end end + + describe '#latest_sector_benchmarks_before_last_assessment' do + let(:sector_cp_benchmarks) do + [ + create(:cp_benchmark, release_date: '2013-05-01', scenario: 'Below 2'), # previous benchmarks + create(:cp_benchmark, release_date: '2013-05-01', scenario: 'Paris'), # - should be ignored + create(:cp_benchmark, release_date: '2013-05-01', scenario: '2 degrees'), + + create(:cp_benchmark, release_date: '2016-05-01', scenario: 'Below 2'), # last benchmarks + create(:cp_benchmark, release_date: '2016-05-01', scenario: 'Paris'), # before latest CP assessment + create(:cp_benchmark, release_date: '2016-05-01', scenario: '2 degrees'), # - OK + + create(:cp_benchmark, release_date: '2019-05-01', scenario: 'Below 2'), # most recent benchmarks, + create(:cp_benchmark, release_date: '2019-05-01', scenario: 'Paris'), # but without CP assessment + create(:cp_benchmark, release_date: '2019-05-01', scenario: '2 degrees') # - should be ignored + ] + end + let(:company_assessments) do + [ + create(:cp_assessment, assessment_date: '2012-05-01'), + create(:cp_assessment, assessment_date: '2013-05-01'), + create(:cp_assessment, assessment_date: '2018-05-01') # <- last assessment date + ] + end + let(:older_company_assessments) do + [ + create(:cp_assessment, assessment_date: '2011-05-01'), + create(:cp_assessment, assessment_date: '2012-05-01') + ] + end + + it 'returns latest CP benchmarks with release date smaller than latest CP Assessment assessment date' do + company = create(:company, + sector: create(:sector, cp_benchmarks: sector_cp_benchmarks), + cp_assessments: company_assessments) + + # last assessment year is 2018, so we should take benchmarks from 2016 + expect(company.latest_sector_benchmarks_before_last_assessment.pluck(:release_date, :scenario)) + .to eq([ + [Date.parse('2016-05-01'), 'Below 2'], + [Date.parse('2016-05-01'), 'Paris'], + [Date.parse('2016-05-01'), '2 degrees'] + ]) + end + + it 'returns latest CP benchmarks with last release date if all CP Assessments are older' do + company = create(:company, + sector: create(:sector, cp_benchmarks: sector_cp_benchmarks), + cp_assessments: older_company_assessments) + + # last assessment year is 2012 - before oldest bechmark, so we just take most recent benchmark + expect(company.latest_sector_benchmarks_before_last_assessment.pluck(:release_date, :scenario)) + .to eq([ + [Date.parse('2019-05-01'), 'Below 2'], + [Date.parse('2019-05-01'), 'Paris'], + [Date.parse('2019-05-01'), '2 degrees'] + ]) + end + end end