Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feature): TPI (sectors, companies) controllers, chart API & views #57

Merged
merged 56 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
22c08c5
[TPI frontend] Sectors dashboard PoC with chartkick
kowal Sep 13, 2019
2dcf729
add Carbon Performance: All sectors chart
kowal Sep 14, 2019
687c81e
add navigation to sectors and companies
kowal Sep 14, 2019
be13b1a
Sector details page with 1st chart
kowal Sep 14, 2019
f01c28c
remove empty files
kowal Sep 14, 2019
a4c1252
sectors controller cleanup
kowal Sep 14, 2019
760d91d
sector levels column "chart"
kowal Sep 15, 2019
0f5a94c
add CP perf. per Setor chart (random data)
kowal Sep 15, 2019
ba1ce9f
use real data on CP perf. chart for sector; add tpi stylesheets
kowal Sep 16, 2019
6cf35f7
sector names
kowal Sep 16, 2019
43ad9e6
clean up sectors controller and views for charts
kowal Sep 17, 2019
276cd4b
extract API services
kowal Sep 17, 2019
7fcf862
dedicated endpoints for charts
kowal Sep 17, 2019
09339d4
comments
kowal Sep 17, 2019
c2bb2fe
better comments
kowal Sep 17, 2019
6a69049
remove empty files
kowal Sep 17, 2019
10d9d7a
clean up
kowal Sep 18, 2019
000a50c
draft of company show page
kowal Sep 18, 2019
a3fa27e
split page sections in visible way
kowal Sep 19, 2019
772fbf2
emissions chart for given company
kowal Sep 19, 2019
ada749d
company CP chart improvements
kowal Sep 19, 2019
7d82914
remove old file
kowal Sep 19, 2019
ed8f484
refactor chart api services
kowal Sep 20, 2019
ae2ea94
simplify company emissions chart data generation
kowal Sep 20, 2019
698877f
renaming
kowal Sep 20, 2019
7b846b0
calculate sector average emissions for company CP chart
kowal Sep 20, 2019
e44c6d5
rename routes for charts
kowal Sep 20, 2019
e3cf354
extract common charts options to ChartHelper
kowal Sep 20, 2019
9b92c94
Set company in Api::Charts::Company constructor
kowal Sep 23, 2019
a8063fc
add CP benchmarks to companies emissions chart
kowal Sep 23, 2019
52f1652
companies_scope in sectors contoller can be private
kowal Sep 23, 2019
59c6117
extract questions_by_level to MQ::Assessment model
kowal Sep 23, 2019
2c40777
extrac Api::Presenters::Company
kowal Sep 23, 2019
a037323
move everything under :tpi namespace
kowal Sep 23, 2019
b3915ce
company cp|mq assessments are ordered with latest first (by assessmen…
kowal Sep 24, 2019
fc53e70
calculate sector avg emissions for company latest CP assessments (not…
kowal Sep 26, 2019
a6e1c04
remove ordering from scope
kowal Sep 27, 2019
4e3eb5a
First version of "Carbon Performance: All sectors" chart with real data
agwozdowski Sep 24, 2019
5436087
Fix rubocop error
agwozdowski Sep 24, 2019
96adf8c
Fix case when company does not have any emission reported
agwozdowski Sep 25, 2019
473f8b6
Move cp_benchmark chart to another file
agwozdowski Sep 25, 2019
207554e
Unit tests for `Carbon Performance: All sectors` chart
agwozdowski Sep 25, 2019
28b7fb6
Documentation for `Carbon Performance: All sectors`
agwozdowski Sep 25, 2019
28c043e
Refactoring for `Carbon Performance: All sectors`
agwozdowski Sep 25, 2019
f3e2ddd
Unit tests for company.rb charts
agwozdowski Sep 25, 2019
518aced
Unit tests for sector.rb charts
agwozdowski Sep 25, 2019
69937c7
One more corner case with more unit tests
agwozdowski Sep 26, 2019
2bbddf8
Fix Rubocop error
agwozdowski Sep 26, 2019
1b2722e
Get CP Benchmarks from last release
agwozdowski Sep 27, 2019
8db1d0a
Fix tests after rebase
agwozdowski Sep 27, 2019
e5b466e
Fix rubocop
agwozdowski Sep 27, 2019
e263e6f
Eager loading and refactoring
agwozdowski Sep 27, 2019
80eb81f
update mq_assessment.questions usage
kowal Sep 27, 2019
21a86c0
revert changes
kowal Sep 27, 2019
81bf1bb
some renames and docs update
kowal Sep 27, 2019
08e932b
(chart-api) fix for fetching sector benchmarks + clean up (#75)
kowal Sep 30, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -468,6 +469,7 @@ DEPENDENCIES
capistrano-rvm
capistrano-yarn
capybara
chartkick
climate_watch_engine (~> 1.4.3)!
devise (>= 4.7.1)
discard
Expand Down
1 change: 1 addition & 0 deletions app/admin/cp_assessments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

index do
column :title, &:title_link
column :company
column :assessment_date
column :publication_date
actions
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require chartkick
//= require highcharts
//= require_tree .
19 changes: 19 additions & 0 deletions app/assets/stylesheets/tpi.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.container {
display: flex;
flex-direction: row;
justify-content: center;
gap: 5px;
kowal marked this conversation as resolved.
Show resolved Hide resolved
}

container-columns {
display: flex;
flex-direction: column;
justify-content: center;
gap: 5px;
}

.chart {
display: flex;
flex-flow: row wrap;
justify-content: center;
}
21 changes: 21 additions & 0 deletions app/controllers/tpi/companies_controller.rb
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions app/controllers/tpi/sectors_controller.rb
Original file line number Diff line number Diff line change
@@ -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_by_level
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_by_level

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::CPBenchmark.new.cp_performance_data

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
34 changes: 34 additions & 0 deletions app/helpers/chart_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module ChartHelper
kowal marked this conversation as resolved.
Show resolved Hide resolved
# 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} <br> {point.y} companies <br> {point.percentage:.1f}%'
}
}
}
}
end
end
41 changes: 39 additions & 2 deletions app/models/company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,48 @@ 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 to_s
name
def latest_cp_assessment
cp_assessments.order(:assessment_date).last
end

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 [#<CP::Benchmark]
#
# @example Company has assessment:
# - benchmarks available for 04.2017 and 05.2018
# - if assessment date is 06.2018 - we take benchmarks from 05.2018
# - if assessment date is 06.2017 - we take benchmarks from 04.2017
def latest_sector_benchmarks_before_last_assessment
last_assessment_date = latest_cp_assessment&.assessment_date

return sector.latest_released_benchmarks unless last_assessment_date

sector_benchmarks_dates = sector.cp_benchmarks.pluck(:release_date).uniq.sort

last_release_date_before_assessment =
sector_benchmarks_dates
.select { |d| d < last_assessment_date }
.last

release_date =
last_release_date_before_assessment || sector_benchmarks_dates.last

sector.cp_benchmarks.where(release_date: release_date)
end
end
4 changes: 4 additions & 0 deletions app/models/mq/assessment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(&:level)
end
end
end
4 changes: 4 additions & 0 deletions app/models/sector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ class Sector < ApplicationRecord

validates_presence_of :name, :slug
validates_uniqueness_of :name, :slug

def latest_released_benchmarks
cp_benchmarks.group_by(&:release_date).max&.last || []
end
end
104 changes: 104 additions & 0 deletions app/services/api/charts/company.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module Api
module Charts
class Company
def initialize(company)
@company = company
end

# Returns array of following series:
# - company emissions
# - company's sector average emissions
# - company's sector CP benchmarks (scenarios)
#
# @return [Array]
# @example
# [
# { 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
[
emissions_data_from_company,
emissions_data_from_sector,
emissions_data_from_sector_benchmarks
].flatten
end

private

def emissions_data_from_company
{
name: @company.name,
data: @company.latest_cp_assessment&.emissions
}
end

def emissions_data_from_sector
{
name: "#{@company.sector.name} sector mean",
data: sector_average_emissions
}
end

def emissions_data_from_sector_benchmarks
@company.latest_sector_benchmarks_before_last_assessment.map do |benchmark|
{
type: 'area',
fillOpacity: 0.1,
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.
#
# @example {'2014' => 111.0, '2015' => 112.0 }
#
def sector_average_emissions
years_with_reported_emissions
.map { |year| [year.to_s, sector_average_emission_for_year(year)] }
.to_h
end

# @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] }
.compact

(company_emissions.sum / company_emissions.count).round(2)
end

# @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<Hash>] list of { year => value } pairs from all Companies from current Sector
def sector_all_emissions
kowal marked this conversation as resolved.
Show resolved Hide resolved
@sector_all_emissions ||= @company.sector
.companies
.includes(:cp_assessments)
.flat_map { |c| c.latest_cp_assessment.emissions }
end
end
end
end
Loading