diff --git a/app/admin/ascor/assessment_indicators.rb b/app/admin/ascor/assessment_indicators.rb new file mode 100644 index 000000000..ff66e1727 --- /dev/null +++ b/app/admin/ascor/assessment_indicators.rb @@ -0,0 +1,58 @@ +ActiveAdmin.register ASCOR::AssessmentIndicator do + config.sort_order = 'id_asc' + + menu label: 'Assessment Indicators', parent: 'ASCOR', priority: 4 + + actions :all, except: [:new, :create] + + filter :code + filter :text + filter :indicator_type, as: :check_boxes, collection: ASCOR::AssessmentIndicator::INDICATOR_TYPES + + data_export_sidebar 'ASCORAssessmentIndicators', display_name: 'ASCOR AssessmentIndicators' + + permit_params :code, :indicator_type, :text + + show do + attributes_table do + row :id + row :code + row :indicator_type + row :text + row :units_or_response_type + row :created_at + row :updated_at + end + + active_admin_comments + end + + form html: {'data-controller' => 'check-modified'} do |f| + f.semantic_errors(*f.object.errors.keys) + + f.inputs do + f.input :indicator_type, as: :select, collection: ASCOR::AssessmentIndicator::INDICATOR_TYPES + f.input :code + f.input :text + f.input :units_or_response_type + end + + f.actions + end + + index do + id_column + column :indicator_type + column :code + column :text + actions + end + + csv do + column :id + column :type, &:indicator_type + column :code + column :text + column :units_or_response_type + end +end diff --git a/app/admin/ascor/assessments.rb b/app/admin/ascor/assessments.rb new file mode 100644 index 000000000..89e210b63 --- /dev/null +++ b/app/admin/ascor/assessments.rb @@ -0,0 +1,102 @@ +ActiveAdmin.register ASCOR::Assessment do + config.sort_order = 'assessment_date_desc' + includes :country + + menu label: 'Assessments', parent: 'ASCOR', priority: 5 + + permit_params :country_id, :assessment_date, :publication_date, :notes, + results_attributes: [:id, :assessment_id, :indicator_id, :answer, :source, :year, :_destroy] + + filter :country + filter :assessment_date, as: :select + + data_export_sidebar 'ASCORAssessments', display_name: 'ASCOR Assessments' + + index do + selectable_column + id_column + column :country, sortable: 'ascor_countries.name' + column :assessment_date + column :publication_date + column :notes + + actions + end + + show do + attributes_table do + row :id + row :country + row :assessment_date + row :publication_date + row :notes + row :created_at + row :updated_at + end + + panel 'Assessment Results' do + table_for resource.results.includes(:indicator).order(:indicator_id) do + column(:indicator) + column(:answer) + column(:source) + column(:year) + end + end + + active_admin_comments + end + + form do |f| + f.semantic_errors(*f.object.errors.keys) + + f.inputs do + f.input :country, as: :select, collection: ASCOR::Country.all.order(:name) + f.input :assessment_date, as: :datepicker + f.input :publication_date, as: :datepicker + f.input :notes + end + + f.has_many :results, allow_destroy: true, heading: false do |ff| + ff.inputs 'Assessment Results' do + ff.input :indicator, as: :select, collection: ASCOR::AssessmentIndicator.all.order(:code) + ff.input :answer + ff.input :source + ff.input :year + end + end + + f.actions + end + + csv do + column :id + column :country do |resource| + resource.country.name + end + column :assessment_date + column :publication_date + ASCOR::AssessmentIndicator.where.not(indicator_type: 'pillar') + .where.not(code: %w[EP.1.a.i EP.1.a.ii]).order(:id).all.each do |indicator| + column "#{indicator.indicator_type} #{indicator.code}", humanize_name: false do |resource| + controller.assessment_results[[resource.id, indicator.id]]&.first&.answer + end + end + ASCOR::AssessmentIndicator.where(indicator_type: %w[indicator metric]).order(:id).all.each do |indicator| + column "source #{indicator.indicator_type} #{indicator.code}", humanize_name: false do |resource| + controller.assessment_results[[resource.id, indicator.id]]&.first&.source + end + end + ASCOR::AssessmentIndicator.where(indicator_type: 'metric').order(:id).all.each do |indicator| + column "year #{indicator.indicator_type} #{indicator.code}", humanize_name: false do |resource| + controller.assessment_results[[resource.id, indicator.id]]&.first&.year + end + end + column :notes + end + + controller do + def assessment_results + @assessment_results ||= ASCOR::AssessmentResult.all.group_by { |r| [r.assessment_id, r.indicator_id] } + end + end +end diff --git a/app/admin/ascor/benchmarks.rb b/app/admin/ascor/benchmarks.rb new file mode 100644 index 000000000..0cbc4a6c3 --- /dev/null +++ b/app/admin/ascor/benchmarks.rb @@ -0,0 +1,88 @@ +ActiveAdmin.register ASCOR::Benchmark do + config.sort_order = 'country_id_asc' + includes :country + + menu label: 'Benchmarks', parent: 'ASCOR', priority: 2 + + permit_params :country_id, :publication_date, :emissions_metric, :emissions_boundary, :units, :benchmark_type, :emissions + + filter :country, as: :select, collection: -> { ASCOR::Country.all.order(:name) } + filter :emissions_metric, as: :select, collection: -> { ASCOR::EmissionsMetric::VALUES } + filter :emissions_boundary, as: :select, collection: -> { ASCOR::EmissionsBoundary::VALUES } + filter :benchmark_type, as: :select, collection: -> { ASCOR::BenchmarkType::VALUES } + + data_export_sidebar 'ASCORBenchmarks', display_name: 'ASCOR Benchmarks' + + index do + selectable_column + id_column + column :country, sortable: 'ascor_countries.name' + column :emissions_metric + column :emissions_boundary + column :units + column :benchmark_type + + actions + end + + show do + attributes_table do + row :id + row :country + row :publication_date + row :emissions_metric + row :emissions_boundary + row :units + row :benchmark_type + row :created_at + row :updated_at + end + + panel 'Benchmark emission values' do + render 'admin/cp/emissions_table', emissions: resource.emissions + end + + active_admin_comments + end + + form html: {'data-controller' => 'check-modified with-emission-table-form'} do |f| + f.semantic_errors(*f.object.errors.keys) + + f.inputs do + f.input :country, as: :select, collection: ASCOR::Country.all.order(:name) + f.input :publication_date, as: :datepicker + f.input :emissions_metric, as: :select, collection: ASCOR::EmissionsMetric::VALUES + f.input :emissions_boundary, as: :select, collection: ASCOR::EmissionsBoundary::VALUES + f.input :units + f.input :benchmark_type, as: :select, collection: ASCOR::BenchmarkType::VALUES + f.input :emissions, as: :hidden, input_html: {value: f.object.emissions.to_json, id: 'input_emissions'} + end + + div class: 'panel' do + h3 'Benchmark emission values' + div class: 'panel-contents padding-20' do + render 'admin/cp/emissions_table_edit', f: f + end + end + + f.actions + end + + csv do + year_columns = ASCOR::Benchmark.select(:emissions).flat_map(&:emissions_all_years).uniq.sort + + column :id + column(:country) { |b| b.country.name } + column(:publication_date) { |b| b.publication_date.to_s(:year_month) } + column :emissions_metric + column :emissions_boundary + column :units + column :benchmark_type + + year_columns.map do |year| + column year do |benchmark| + benchmark.emissions[year] + end + end + end +end diff --git a/app/admin/ascor/countries.rb b/app/admin/ascor/countries.rb new file mode 100644 index 000000000..cf329134c --- /dev/null +++ b/app/admin/ascor/countries.rb @@ -0,0 +1,65 @@ +ActiveAdmin.register ASCOR::Country do + config.batch_actions = false + config.sort_order = 'name_asc' + + menu label: 'Countries', parent: 'ASCOR', priority: 1 + + permit_params :name, :iso, :region, :wb_lending_group, :fiscal_monitor_category, :type_of_party + + filter :iso_contains, label: 'ISO' + filter :name_contains, label: 'Name' + filter :region, as: :check_boxes, collection: proc { Geography::REGIONS } + + data_export_sidebar 'ASCORCountries', display_name: 'ASCOR Countries' + + index do + selectable_column + id_column + column :name + column 'Country ISO code', :iso + column :region + + actions + end + + show do + attributes_table do + row :id + row :name + row 'Country ISO code', &:iso + row :region + row 'World Bank lending group', &:wb_lending_group + row 'International Monetary Fund fiscal monitor category', &:fiscal_monitor_category + row 'Type of Party to the United Nations Framework Convention on Climate Change', &:type_of_party + end + + active_admin_comments + end + + form do |f| + semantic_errors(*f.object.errors.attribute_names) + + f.inputs do + f.input :name + f.input :iso, label: 'Country ISO code' + f.input :region, as: :select, collection: ASCOR::Country::REGIONS + f.input :wb_lending_group, as: :select, collection: ASCOR::Country::LENDING_GROUPS, label: 'World Bank lending group' + f.input :fiscal_monitor_category, as: :select, collection: ASCOR::Country::MONITOR_CATEGORIES, + label: 'International Monetary Fund fiscal monitor category' + f.input :type_of_party, as: :select, collection: ASCOR::Country::TYPE_OF_PARTY, + label: 'Type of Party to the United Nations Framework Convention on Climate Change' + end + + f.actions + end + + csv do + column :id + column :name + column 'Country ISO code', humanize_name: false, &:iso + column :region + column 'World Bank lending group', humanize_name: false, &:wb_lending_group + column 'International Monetary Fund fiscal monitor category', humanize_name: false, &:fiscal_monitor_category + column 'Type of Party to the United Nations Framework Convention on Climate Change', humanize_name: false, &:type_of_party + end +end diff --git a/app/admin/ascor/pathways.rb b/app/admin/ascor/pathways.rb new file mode 100644 index 000000000..3b51289df --- /dev/null +++ b/app/admin/ascor/pathways.rb @@ -0,0 +1,116 @@ +ActiveAdmin.register ASCOR::Pathway do + config.sort_order = 'id_asc' + includes :country + + menu label: 'Pathways', parent: 'ASCOR', priority: 3 + + permit_params :country_id, :publication_date, :assessment_date, :emissions_metric, :emissions_boundary, :units, + :emissions, :last_historical_year, :trend_1_year, :trend_3_year, :trend_5_year, :trend_source, :trend_year, + :recent_emission_level, :recent_emission_source, :recent_emission_year + + filter :country, as: :select, collection: -> { ASCOR::Country.all.order(:name) } + filter :assessment_date, as: :select, collection: -> { ASCOR::Pathway.pluck(:assessment_date).uniq } + filter :emissions_metric, as: :select, collection: -> { ASCOR::EmissionsMetric::VALUES } + filter :emissions_boundary, as: :select, collection: -> { ASCOR::EmissionsBoundary::VALUES } + + data_export_sidebar 'ASCORPathways', display_name: 'ASCOR Pathways' + + index do + selectable_column + id_column + column :country, sortable: 'ascor_countries.name' + column :assessment_date + column :emissions_metric + column :emissions_boundary + column :units + + actions + end + + show do + attributes_table do + row :id + row :country + row :assessment_date + row :publication_date + row :emissions_metric + row :emissions_boundary + row :units + row :last_historical_year + row 'metric EP1.a.i', &:recent_emission_level + row 'source EP1.a.i', &:recent_emission_source + row 'year EP1.a.i', &:recent_emission_year + row 'metric EP1.a.ii 1-year', &:trend_1_year + row 'metric EP1.a.ii 3-year', &:trend_3_year + row 'metric EP1.a.ii 5-year', &:trend_5_year + row 'source metric EP1.a.ii', &:trend_source + row 'year metric EP1.a.ii', &:trend_year + row :created_at + row :updated_at + end + + panel 'Pathway emission values' do + render 'admin/cp/emissions_table', emissions: resource.emissions + end + + active_admin_comments + end + + form html: {'data-controller' => 'check-modified with-emission-table-form'} do |f| + f.semantic_errors(*f.object.errors.keys) + + f.inputs do + f.input :country, as: :select, collection: ASCOR::Country.all.order(:name) + f.input :assessment_date, as: :datepicker + f.input :publication_date, as: :datepicker + f.input :emissions_metric, as: :select, collection: ASCOR::EmissionsMetric::VALUES + f.input :emissions_boundary, as: :select, collection: ASCOR::EmissionsBoundary::VALUES + f.input :units + f.input :last_historical_year + f.input :recent_emission_level, label: 'metric EP1.a.i' + f.input :recent_emission_source, label: 'source EP1.a.i' + f.input :recent_emission_year, label: 'year EP1.a.i' + f.input :trend_1_year, label: 'metric EP1.a.ii 1-year' + f.input :trend_3_year, label: 'metric EP1.a.ii 3-year' + f.input :trend_5_year, label: 'metric EP1.a.ii 5-year' + f.input :trend_source, label: 'source metric EP1.a.ii' + f.input :trend_year, label: 'year metric EP1.a.ii' + f.input :emissions, as: :hidden, input_html: {value: f.object.emissions.to_json, id: 'input_emissions'} + end + + div class: 'panel' do + h3 'Pathway emission values' + div class: 'panel-contents padding-20' do + render 'admin/cp/emissions_table_edit', f: f + end + end + + f.actions + end + + csv do + year_columns = ASCOR::Pathway.select(:emissions).flat_map(&:emissions_all_years).uniq.sort + + column :id + column(:country) { |b| b.country.name } + column(:assessment_date) { |b| b.assessment_date.strftime '%m/%d/%y' } + column(:publication_date) { |b| b.publication_date.to_s(:year_month) } + column :emissions_metric + column :emissions_boundary + column :units + column :last_historical_year + column 'metric EP1.a.i', humanize_name: false, &:recent_emission_level + column 'source EP1.a.i', humanize_name: false, &:recent_emission_source + column 'year EP1.a.i', humanize_name: false, &:recent_emission_year + column 'metric EP1.a.ii 1-year', humanize_name: false, &:trend_1_year + column 'metric EP1.a.ii 3-year', humanize_name: false, &:trend_3_year + column 'metric EP1.a.ii 5-year', humanize_name: false, &:trend_5_year + column 'source metric EP1.a.ii', humanize_name: false, &:trend_source + column 'year metric EP1.a.ii', humanize_name: false, &:trend_year + year_columns.map do |year| + column year do |resource| + resource.emissions[year] + end + end + end +end diff --git a/app/admin/data_uploads.rb b/app/admin/data_uploads.rb index 4823898f2..9613f2ce7 100644 --- a/app/admin/data_uploads.rb +++ b/app/admin/data_uploads.rb @@ -1,5 +1,5 @@ ActiveAdmin.register DataUpload do - menu parent: 'Administration', priority: 2 + menu parent: 'Administration', priority: 3 decorate_with DataUploadDecorator diff --git a/app/assets/images/tpi/ascor/circles_1.svg b/app/assets/images/tpi/ascor/circles_1.svg new file mode 100644 index 000000000..8235f39cf --- /dev/null +++ b/app/assets/images/tpi/ascor/circles_1.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/tpi/ascor/circles_2.svg b/app/assets/images/tpi/ascor/circles_2.svg new file mode 100644 index 000000000..18ecfa14a --- /dev/null +++ b/app/assets/images/tpi/ascor/circles_2.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/tpi/ascor/circles_3.svg b/app/assets/images/tpi/ascor/circles_3.svg new file mode 100644 index 000000000..056420b12 --- /dev/null +++ b/app/assets/images/tpi/ascor/circles_3.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/tpi/ascor/excempt.svg b/app/assets/images/tpi/ascor/excempt.svg new file mode 100644 index 000000000..c99fdf445 --- /dev/null +++ b/app/assets/images/tpi/ascor/excempt.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/tpi/ascor/logo.png b/app/assets/images/tpi/ascor/logo.png new file mode 100644 index 000000000..26a4aeedb Binary files /dev/null and b/app/assets/images/tpi/ascor/logo.png differ diff --git a/app/assets/images/tpi/ascor/no.svg b/app/assets/images/tpi/ascor/no.svg new file mode 100644 index 000000000..b8c1e2cf0 --- /dev/null +++ b/app/assets/images/tpi/ascor/no.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/tpi/ascor/no_data.svg b/app/assets/images/tpi/ascor/no_data.svg new file mode 100644 index 000000000..08ef2dbcd --- /dev/null +++ b/app/assets/images/tpi/ascor/no_data.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/images/tpi/ascor/no_disclosure.svg b/app/assets/images/tpi/ascor/no_disclosure.svg new file mode 100644 index 000000000..329cdbf62 --- /dev/null +++ b/app/assets/images/tpi/ascor/no_disclosure.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/tpi/ascor/not_applicable.svg b/app/assets/images/tpi/ascor/not_applicable.svg new file mode 100644 index 000000000..717566b52 --- /dev/null +++ b/app/assets/images/tpi/ascor/not_applicable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/tpi/ascor/partial.svg b/app/assets/images/tpi/ascor/partial.svg new file mode 100644 index 000000000..58dad66db --- /dev/null +++ b/app/assets/images/tpi/ascor/partial.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/tpi/ascor/yes.svg b/app/assets/images/tpi/ascor/yes.svg new file mode 100644 index 000000000..973121c83 --- /dev/null +++ b/app/assets/images/tpi/ascor/yes.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/stylesheets/tpi.scss b/app/assets/stylesheets/tpi.scss index 3c48f0964..d3605b988 100644 --- a/app/assets/stylesheets/tpi.scss +++ b/app/assets/stylesheets/tpi.scss @@ -26,8 +26,10 @@ @import "tpi/navbar"; @import "tpi/publications"; @import "tpi/bubble-chart"; +@import "tpi/bubble-chart-countries"; @import "tpi/charts"; @import "tpi/base-tooltip"; +@import "tpi/info-tooltip"; @import "tpi/filters"; @import "tpi/footer"; @import "tpi/dropdown-selector"; @@ -37,9 +39,12 @@ @import "tpi/fixed-navbar"; @import "tpi/nested-dropdown"; @import "tpi/banking-question-legend"; +@import "tpi/country-question-legend"; @import "tpi/latest-information"; @import "tpi/mq_beta_scores"; @import "tpi/mq-beta-modal"; +@import "tpi/selector"; +@import "tpi/emissions-chart"; @import "tpi/pages/*"; @import "tpi/shared/*"; diff --git a/app/assets/stylesheets/tpi/_bubble-chart-countries.scss b/app/assets/stylesheets/tpi/_bubble-chart-countries.scss new file mode 100644 index 000000000..f1faf39ee --- /dev/null +++ b/app/assets/stylesheets/tpi/_bubble-chart-countries.scss @@ -0,0 +1,195 @@ +@import "colors"; +@import "typography"; +$tape-height: 8px; +$tape-color: rgba(25, 25, 25, 0.1); +$cell-height: 80px; +$cell-height-banks: 100px; +$legend-image-width: 60px; + +.bubble-chart__container__grid { + display: none; + @include desktop { + grid-template-columns: 0.5fr 0.5fr 1.5fr 1fr 1fr 1fr; + padding: 0; + display: grid; + } +} + +.bubble-chart__container__mobile { + display: block; + width: 100%; + padding: 10px; + + .country-bubble-mobile { + width: 100%; + border: 1px solid $ascor-background-color; + + > ul > li:last-of-type .country-bubble-mobile__item { + border-bottom: none; + } + + &__item { + width: 100%; + display: flex; + justify-content: space-between; + font-size: 16px; + font-style: normal; + + &.--pillar { + font-weight: 700; + font-family: $font-family-bold; + background: $ascor-background-color; + color: #fff; + padding: 20px 10px; + cursor: pointer; + border-bottom: 1px solid $grey-medium; + &.--open { + border-bottom: none; + } + } + &.--area { + color: #000; + padding: 10px; + font-family: $font-family-regular; + border-top: 1px solid $ascor-background-color; + cursor: pointer; + &.--open { + border-bottom: 1px solid $grey-medium; + } + } + + .chevron-icon { + width: 12px; + } + + &__result { + font-family: $font-family-regular; + font-size: 16px; + + &__title { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + padding: 10px; + border-bottom: 1px solid $grey-medium; + cursor: pointer; + min-height: 45px; + position: relative; + + div { + width: 20px; + height: 20px; + border-radius: 50%; + } + } + + &:not(:last-of-type) .country-bubble-mobile__item__result__title { + border-bottom-style: dashed; + } + + &.--open { + .country-bubble-mobile__item__result__title { + border-bottom: none; + position: absolute; + } + .country-bubble-mobile__item__result__countries { + border-bottom: 1px solid $grey-medium; + border-bottom-style: dashed; + } + } + + &:last-of-type { + .country-bubble-mobile__item__result__countries { + border-bottom: none; + } + } + + &__countries { + padding: 10px 0px 10px 42px; + min-height: 45px; + li:not(:last-of-type) { + padding-bottom: 10px; + } + } + } + } + } +} + +.bubble-chart__cell-country { + position: relative; + height: $cell-height-banks; + display: flex; + align-items: center; + border-right: calc(#{$tape-height / 2}) dashed $tape-color; + + & > *:first-child { + margin: auto; + z-index: 1; + } + + &::before { + background-color: $tape-color; + content: ""; + position: absolute; + top: calc(50% - #{$tape-height / 2}); + height: $tape-height; + width: calc(100% + #{$tape-height / 2}); + } +} + +.bubble-chart_circle_country { + circle:hover { + stroke-width: 3; + stroke: $black !important; + } +} + +.bubble-chart__level-country { + border-right: calc(#{$tape-height / 2}) dashed $tape-color; + position: relative; + padding-left: 20px; + height: 100%; +} + +.bubble-chart__level-title-country { + height: 100%; + font-family: $font-family-bold; + font-size: 16px; + color: $black; + margin-bottom: 20px; +} + +.bubble-chart__level-area-country { + font-family: $font-family-bold; + font-size: 16px; + background-color: $ascor-background-color; + color: white; + padding: 10px; + width: 100%; + display: flex; + align-items: center; + + color: $black; + text-align: end; + margin-right: 14px; + grid-column: span 2; + height: 100%; + padding: 46px 0 46px; + gap: 16px; + background-color: white; + + &__line { + border: 8px solid #e8e8e8; + border-right: none; + height: 100%; + flex: 1; + } + + &__area { + text-align: end; + padding-right: 14px; + flex: 1; + } +} diff --git a/app/assets/stylesheets/tpi/_bubble-chart.scss b/app/assets/stylesheets/tpi/_bubble-chart.scss index f5b1e6b70..67189583b 100644 --- a/app/assets/stylesheets/tpi/_bubble-chart.scss +++ b/app/assets/stylesheets/tpi/_bubble-chart.scss @@ -2,7 +2,7 @@ @import "typography"; $tape-height: 8px; -$tape-color: rgba(25,25,25,0.1); +$tape-color: rgba(25, 25, 25, 0.1); $cell-height: 80px; $cell-height-banks: 100px; $legend-image-width: 60px; @@ -21,7 +21,6 @@ $legend-image-width: 60px; .last { border-right: none; } - } &--banks { diff --git a/app/assets/stylesheets/tpi/_colors.scss b/app/assets/stylesheets/tpi/_colors.scss index 2439537ff..9a6c54e82 100644 --- a/app/assets/stylesheets/tpi/_colors.scss +++ b/app/assets/stylesheets/tpi/_colors.scss @@ -8,6 +8,7 @@ $hawkes-blue: #ECF1FE; $yellow: #FFDD49; $grey-light: #DDE7FD; $grey-medium: #D8D8D8; +$grey-lighter-medium: #E2E2E2; $grey: #F5F8FE; $grey-dark: #595B5D; $grey-blue: #CFD7ED; diff --git a/app/assets/stylesheets/tpi/_emissions-chart.scss b/app/assets/stylesheets/tpi/_emissions-chart.scss new file mode 100644 index 000000000..46a8e8074 --- /dev/null +++ b/app/assets/stylesheets/tpi/_emissions-chart.scss @@ -0,0 +1,189 @@ +@import "variables"; + +.emissions { + padding-bottom: 30px; + &__filters { + margin-bottom: 66px; + display: flex; + flex-wrap: wrap; + gap: 12px; + justify-content: space-between; + + &.assessments { + margin-top: 10px; + margin-bottom: 0px; + } + + &__emissions { + display: flex; + flex-wrap: wrap; + gap: 12px; + } + + &__country-selector { + .button { + padding: 12px 16px; + } + + &__countries { + height: 0; + overflow: hidden; + + &.--countries-open { + overflow: visible; + } + + &__wrapper { + z-index: 10; + position: relative; + top: 18px; + padding: 24px; + border: 1px solid $grey-dark; + background: #fff; + } + + &__label { + color: $grey-dark; + font-family: $family-sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 15px; + } + + &__list { + max-height: 300px; + overflow-y: auto; + margin-block: 24px; + position: relative; + + li { + display: flex; + gap: 20px; + align-items: center; + margin-bottom: 16px; + + input { + width: 24px; + height: 24px; + } + } + } + + &__button { + display: flex; + justify-content: flex-end; + } + } + } + } + &__chart { + > div, + .highcharts-container { + overflow: visible !important; + + @include until($breakpoint-desktop) { + .highcharts-axis-title { + transform: translateY(-15px) !important; + left: 0 !important; + } + } + } + + &__tooltip { + > span { + background-color: #fff; + padding: 24px; + border: 1px solid $grey-dark; + } + &__header { + display: flex; + justify-content: space-between; + color: $dark; + font-family: $family-sans-serif; + font-size: 12px; + font-weight: 700; + margin-bottom: 12px; + } + &__item { + display: flex; + justify-content: space-between; + gap: 30px; + > div { + display: flex; + align-items: center; + gap: 10px; + } + .--target { + color: $grey-dark; + font-weight: 400; + } + } + } + + &__legend { + margin-top: 60px; + display: flex; + flex-wrap: wrap; + gap: 12px; + @include until($breakpoint-desktop) { + margin-top: 30px; + } + + &__label { + display: flex; + justify-content: center; + gap: 8px; + padding: 7px 12px; + border: 1px solid rgba(89, 91, 93, 0.5); + color: $grey-dark; + font-family: $family-sans-serif; + font-size: 12px; + + @include until($breakpoint-desktop) { + // margin-top: 30px; + padding: 6px 10px; + font-size: 10px; + } + + span { + width: 16px; + height: 16px; + border-radius: 50%; + display: inline-block; + + @include until($breakpoint-desktop) { + width: 12px; + height: 12px; + } + } + } + } + .highcharts-legend > div > div { + display: flex; + gap: 12px; + flex-wrap: wrap; + width: 80vw; + margin-top: 44px; + + @include until($breakpoint-desktop) { + margin-top: 24px; + } + + .highcharts-legend-item.highcharts-line-series { + position: relative !important; + top: 0 !important; + left: 0 !important; + + span { + position: relative !important; + top: 0 !important; + left: 0 !important; + } + } + .highcharts-legend-item-hidden { + opacity: 0.5 !important; + } + } + } +} diff --git a/app/assets/stylesheets/tpi/_info-tooltip.scss b/app/assets/stylesheets/tpi/_info-tooltip.scss new file mode 100644 index 000000000..62ad7a806 --- /dev/null +++ b/app/assets/stylesheets/tpi/_info-tooltip.scss @@ -0,0 +1,4 @@ +.info-tooltip { + position: relative; + max-width: 200px; +} diff --git a/app/assets/stylesheets/tpi/_mq_beta_scores.scss b/app/assets/stylesheets/tpi/_mq_beta_scores.scss index 9c61a68de..d7078a942 100644 --- a/app/assets/stylesheets/tpi/_mq_beta_scores.scss +++ b/app/assets/stylesheets/tpi/_mq_beta_scores.scss @@ -41,6 +41,13 @@ } } + &__beta-button { + &.disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; + } + } &__beta-button:before { content: ""; diff --git a/app/assets/stylesheets/tpi/_nested-dropdown.scss b/app/assets/stylesheets/tpi/_nested-dropdown.scss index d56d69ce4..ae10a5794 100644 --- a/app/assets/stylesheets/tpi/_nested-dropdown.scss +++ b/app/assets/stylesheets/tpi/_nested-dropdown.scss @@ -111,4 +111,13 @@ color: $white; } } + + &--ascor:not(.nested-dropdown--open) { + background: $ascor-background-color; + + .nested-dropdown__title { + border: 1px solid $white; + color: $white; + } + } } diff --git a/app/assets/stylesheets/tpi/_publications.scss b/app/assets/stylesheets/tpi/_publications.scss index f8f9fd804..e303d03f2 100644 --- a/app/assets/stylesheets/tpi/_publications.scss +++ b/app/assets/stylesheets/tpi/_publications.scss @@ -114,7 +114,7 @@ $max-lines: 3; } } -.tpi-sector__promoted-publications, .tpi-banks__promoted-publications { +.tpi-sector__promoted-publications, .tpi-banks__promoted-publications, .ascor-page__promoted-publications { margin-bottom: 30px; .view-all-btn__container { diff --git a/app/assets/stylesheets/tpi/_selector.scss b/app/assets/stylesheets/tpi/_selector.scss new file mode 100644 index 000000000..7939a319a --- /dev/null +++ b/app/assets/stylesheets/tpi/_selector.scss @@ -0,0 +1,80 @@ +@import "variables"; + +.selector__wrapper { + .selector__container { + min-width: 200px; + max-width: 300px; + + .selector__header { + border: 1px solid #595b5d; + padding: 8px 16px; + display: flex; + justify-content: space-between; + cursor: pointer; + + .selector__value { + width: 100%; + margin-right: 16px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .chevron-icon { + width: 16px; + height: 14px; + } + + .chevron-icon-rotated { + transform: rotate(180deg); + } + } + + .selector__header--active { + border-bottom: none; + } + + .selector__options-wrapper { + height: 0; + overflow: hidden; + + &--open { + overflow: visible; + } + + .selector__options { + z-index: 10; + position: relative; + border: 1px solid #595b5d; + background-color: #fff; + padding-block: 10px; + + .selector__option { + padding-inline: 16px; + color: $ascor-color; + font-family: $family-sans-serif; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 20px; + padding-block: 10px; + + &.selector__option--selected { + background-color: $ascor-background-color; + color: #fff; + font-size: 18px; + font-weight: 600; + } + + &:hover { + background-color: $ascor-background-color; + color: #fff; + } + } + } + } + } + + .selector__container--active { + } +} diff --git a/app/assets/stylesheets/tpi/_variables.scss b/app/assets/stylesheets/tpi/_variables.scss index bc717dba7..9f71fa7ab 100644 --- a/app/assets/stylesheets/tpi/_variables.scss +++ b/app/assets/stylesheets/tpi/_variables.scss @@ -71,6 +71,9 @@ $navbar-dropdown-item-hover-background-color: $blue; $navbar-dropdown-item-active-color: white; $navbar-dropdown-item-active-background-color: $blue; $navbar-bottom-box-shadow-size: 0; +$ascor-background-color: #242638; +$ascor-color: #191919; +$ascor-green: #17B091; // TAGS $tag-background-color: $white; diff --git a/app/assets/stylesheets/tpi/country-question-legend.scss b/app/assets/stylesheets/tpi/country-question-legend.scss new file mode 100644 index 000000000..483f5d52e --- /dev/null +++ b/app/assets/stylesheets/tpi/country-question-legend.scss @@ -0,0 +1,107 @@ +.country-question-legend { + border: 1px solid $grey-lighter-medium; + background-color: $white; + position: fixed; + + bottom: 0; + right: 0; + + font-size: 12px; + + z-index: 10; + + transition: opacity .5s ease-out; + opacity: 0; + + @include desktop { + bottom: unset; + right: unset; + top: 50%; + left: 15px; + } + + @media (min-width: 1530px) { + left: unset; + right: calc(#{$widescreen} + (100vw - #{$widescreen}) / 2); + } + + &--active { + opacity: 1; + } + + &__header { + display: none; + + padding: 10px; + text-transform: uppercase; + border-bottom: 1px solid #CFD7ED; + + @include desktop { + display: block; + } + } + + &__content { + padding: 15px; + display: flex; + gap: 15px; + + @include desktop { + flex-direction: column; + } + + .country-question-legend-answer { + display: flex; + align-items: center; + gap: 10px; + + &:before { + content: ''; + display: block; + margin-top: -3px; + + width: 20px; + height: 20px; + border-radius: 50%; + background-repeat: no-repeat; + background-position: center; + background-size: 9px; + } + + &--no:before { + background-image: image-url('tpi/ascor/no.svg'); + background-size: 20px; + } + + &--yes:before { + background-image: image-url('tpi/ascor/yes.svg'); + background-size: 20px; + } + + &--not-applicable:before { + background-image: image-url('tpi/ascor/not_applicable.svg'); + background-size: 20px; + } + + &--no-data:before { + background-image: image-url('tpi/ascor/no_data.svg'); + background-size: 20px; + } + + &--partial:before { + background-image: image-url('tpi/ascor/partial.svg'); + background-size: 20px; + } + + &--no-disclosure:before { + background-image: image-url('tpi/ascor/no_disclosure.svg'); + background-size: 20px; + } + + &--excempt:before { + background-image: image-url('tpi/ascor/excempt.svg'); + background-size: 20px; + } + } + } +} diff --git a/app/assets/stylesheets/tpi/pages/ascor.scss b/app/assets/stylesheets/tpi/pages/ascor.scss new file mode 100644 index 000000000..3da31a69e --- /dev/null +++ b/app/assets/stylesheets/tpi/pages/ascor.scss @@ -0,0 +1,571 @@ +$see-more-width: 100px; +$see-more-width-tablet: 130px; + +.ascor-page { + .dropdown-selector-wrapper { + position: relative; + background-color: $ascor-background-color; + + .dropdown-selector__container { + background-color: $ascor-background-color; + + @include desktop { + padding: $container-top-padding $container-side-padding 40px $container-side-padding; + } + } + + .dropdown-selector__container--active { + background-color: $white; + } + + .dropdown-selector__wrapper { + background-color: $ascor-background-color; + } + + .dropdown-selector__active-button { + background-color: $dark; + } + + .dropdown-selector__option { + &:hover { + background-color: $ascor-background-color; + color: #fff; + } + } + + @include desktop { + height: 335px; + } + + .left-icon { + background-image: image-url('tpi/ascor/circles_1.svg'); + width: 100%; + height: 100%; + background-repeat: no-repeat; + position: absolute; + background-position-y: 30px; + } + + .right-icon { + background-image: image-url('tpi/ascor/circles_2.svg'); + width: 100%; + height: 100%; + background-repeat: no-repeat; + position: absolute; + background-position-x: 100%; + } + + .bottom-icon { + background-image: image-url('tpi/ascor/logo.png'); + width: 100%; + height: 100%; + background-repeat: no-repeat; + position: absolute; + background-position: 50px 260px; + } + + .ascor-header { + min-height: 100px; + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + + &.show-version { + justify-content: space-between; + + @include mobile { + justify-content: flex-end; + } + + @include tablet { + padding-left: 20px; + } + + @include desktop { + padding-left: calc((960px - 608px)/2); + } + + @include widescreen { + padding-left: calc((1152px - 608px)/2); + } + } + + &__assessment-dropdown { + display: flex; + gap: 10px; + color: white; + margin-right: 20px; + + .caption { + line-height: 35px; + } + + @include tablet { + flex-direction: row; + align-items: center; + } + } + + .button { + background-color: $ascor-background-color; + } + + .button:hover { + background-color: $dark; + } + + a { + color: white; + margin-right: 20px; + } + + .links { + padding-right: 20px; + } + + @include until($desktop) { + padding: 0 0.75rem; + font-size: $size-7; + height: 60px; + font-family: $font-family-regular; + } + } + } + + .base-tooltip__default-trigger { + background-color: $ascor-green; + + &:hover { + background-color: transparentize($ascor-green, 0.4); + } + } + + .bubble-chart-header { + margin-bottom: 20px; + + @include desktop { + display: flex; + justify-content: space-between; + align-items: center; + } + + &__assessment-dropdown { + display: flex; + align-items: center; + gap: 10px; + } + } + + .bubble-chart-description { + margin-bottom: 40px; + font-size: 14px; + line-height: 1.75; + + @include desktop { + max-width: 500px; + } + } + + .emissions-chart-header { + margin-bottom: 20px; + + @include desktop { + display: flex; + justify-content: space-between; + align-items: center; + } + + &__assessment-dropdown { + display: flex; + align-items: center; + gap: 10px; + } + } + + .emissions-chart-description { + margin-bottom: 40px; + font-size: 14px; + line-height: 1.75; + + @include desktop { + max-width: 500px; + } + } + + section { + margin-top: 70px; + + > p { + margin-top: 30px; + } + + @include until($desktop) { + margin-top: 40px; + padding: 0 0.75rem; + + h4 { + font-size: $size-5 !important; + } + + > p { + margin-top: 15px; + font-size: $size-6; + color: $grey-dark; + } + } + } + + #methodology { + display: flex; + + .pages__content { + margin-bottom: 0; + + > div { + margin-top: 30px; + } + + @include until($desktop) { + h4 { + font-size: $size-5 !important; + } + + > div { + margin-top: 15px; + font-size: $size-6; + color: $grey-dark; + } + } + } + } + + .button { + background-color: $ascor-green; + color: $white; + border: 0; + + &:hover { + color: $white; + background-color: transparentize($ascor-green, 0.4); + } + } + + .country-assessment { + margin-top: 60px; + margin-bottom: 60px; + + &__pillar { + outline: solid 1px $grey-lighter-medium; + margin-bottom: 1px; + padding: 30px 0 0 10px; + + &.active { + background-color: transparentize($ascor-background-color, 0.96); + } + + &__title { + color: $dark; + font-family: $family-sans-serif; + font-size: 24px !important; + line-height: 1.7rem; + font-weight: 400; + text-transform: uppercase; + margin: 5px 0 40px 0; + } + + &__subtitle { + color: $grey-dark; + font-family: $family-sans-serif; + font-size: 12px; + } + + @include tablet { + padding: 30px 0 0 40px; + } + } + + &__area { + outline: solid 1px $grey-lighter-medium; + padding: 15px 20px 30px 20px; + position: relative; + background-color: white; + display: flex; + + &__title { + font-family: $family-sans-serif; + font-size: 20px !important; + display: flex; + align-items: center; + gap: 25px; + + div:first-child { + min-width: 80px; + } + } + + @include tablet { + margin-left: 40px; + padding: 15px 20px; + } + } + + &__icon { + &:before { + content: ''; + display: block; + + min-width: 40px; + min-height: 40px; + border-radius: 50%; + background-repeat: no-repeat; + background-position: center; + background-size: 40px; + } + + &--no:before { + background-image: image-url('tpi/ascor/no.svg'); + } + + &--yes:before { + background-image: image-url('tpi/ascor/yes.svg'); + } + + &--not-applicable:before { + background-image: image-url('tpi/ascor/not_applicable.svg'); + } + + &--no-data:before { + background-image: image-url('tpi/ascor/no_data.svg'); + } + + &--partial:before { + background-image: image-url('tpi/ascor/partial.svg'); + } + + &--no-disclosure:before { + background-image: image-url('tpi/ascor/no_disclosure.svg'); + } + + &--excempt:before { + background-image: image-url('tpi/ascor/excempt.svg'); + } + } + + input.toggle { + display: none; + } + + &__indicators { + display: none; + } + + &__indicator { + margin-bottom: 1px; + outline: solid 1px $grey-lighter-medium; + padding: 15px 45px 15px 20px; + display: flex; + + &__title { + font-family: $family-sans-serif; + font-weight: 700; + font-size: 16px; + display: flex; + align-items: center; + gap: 25px; + flex-grow: 2; + } + + &__source { + font-family: $family-sans-serif; + font-size: 14px !important; + line-height: 40px; + + a { + color: $grey-dark; + text-decoration: underline; + } + + a:hover { + color: $dark; + } + } + + @include tablet { + margin-left: 40px; + } + } + + &__break { + flex-basis: 100%; + height: 0; + } + + &__metric-block { + margin-left: 40px; + padding: 30px 45px 0 45px; + border-left: solid 3px $grey-lighter-medium; + + @include tablet { + margin-left: 80px; + } + } + + &__metric { + display: flex; + padding-bottom: 30px; + position: relative; + flex-wrap: wrap; + + &__title { + flex-grow:2 + } + + &__text { + background: $dark; + padding: 5px 10px; + color: $white; + font-weight: 600; + margin-top: 10px; + border: solid 1px $grey-lighter-medium; + text-align: center; + min-width: 80px; + font-size: small; + } + + &__source { + font-family: $family-sans-serif; + font-size: 14px !important; + + a { + color: $grey-dark; + text-decoration: underline; + } + + a:hover { + color: $dark; + } + } + } + + &__more { + cursor: pointer; + position: absolute; + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 10px; + padding-right: 10px; + height: 30px; + width: $see-more-width; + right: -1px; + bottom: -1px; + border: 1px solid #CFD7ED; + user-select: none; + + @include tablet { + width: $see-more-width-tablet; + } + + &:before { + color: $grey-dark; + content: 'See more'; + display: block; + font-size: 12px; + margin-top: 3px; + } + + &:after { + content: ''; + display: block; + + background-image: image-url('icons/chevron-gray.svg'); + background-repeat: no-repeat; + background-position: right center; + background-size: 16px; + + height: 16px; + width: 16px; + } + } + + input.toggle:checked + .country-assessment__area-block { + .country-assessment__indicators { + display: block; + } + + .country-assessment__more { + &:before { + content: 'See less'; + } + + &::after { + transform: rotate(-180deg); + } + } + } + } + + .contacts { + width: 100%; + height: 380px; + background-color: $ascor-green; + position: relative; + + &__icon { + background-image: image-url('tpi/ascor/circles_3.svg'); + width: 100%; + height: 100%; + background-repeat: no-repeat; + position: absolute; + display: none; + + @include tablet { + background-position-x: calc(50% - 250px); + display: block; + } + + @include widescreen { + background-position-x: 200px; + } + } + + &__info { + padding-top: 80px; + margin: 0 auto; + width: 400px; + + h3 { + font-family: $family-sans-serif; + font-size: 24px !important; + font-weight: 700; + padding: 20px 0; + } + + p { + font-size: 14px; + margin-bottom: 50px + } + + .button { + background-color: $ascor-background-color; + color: $white; + display: inline-block; + + &:hover { + background-color: $dark; + border: 0; + } + } + + &__link { + font-weight: bold; + text-decoration: underline; + } + + @include tablet { + margin: 0 0 0 50%; + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/tpi/pages/home.scss b/app/assets/stylesheets/tpi/pages/home.scss index 83ba30312..aa2ee8190 100644 --- a/app/assets/stylesheets/tpi/pages/home.scss +++ b/app/assets/stylesheets/tpi/pages/home.scss @@ -169,7 +169,7 @@ background-position: right -70px bottom 30px; } - &.sovereign-bonds { + &.ascor { background-color: $blue; color: $white; background-image: image-url('tpi/home/countries.svg'); @@ -805,4 +805,4 @@ width: 100% !important; padding: 0 5px; } -} \ No newline at end of file +} diff --git a/app/controllers/tpi/ascor_controller.rb b/app/controllers/tpi/ascor_controller.rb new file mode 100644 index 000000000..1b9cedcf2 --- /dev/null +++ b/app/controllers/tpi/ascor_controller.rb @@ -0,0 +1,79 @@ +module TPI + class ASCORController < TPIController + before_action :fetch_ascor_countries, only: [:index, :show] + before_action :fetch_ascor_country, only: [:show, :show_assessment] + before_action :fetch_assessment_date, only: [:index, :show, :index_assessment, :show_assessment] + before_action :fetch_emissions_assessment_date, only: [:index, :emissions_chart_data] + before_action :fetch_ascor_assessment_results, only: [:index, :index_assessment] + + def index + @assessment_dates = ASCOR::Assessment.pluck(:assessment_date).uniq + @publications_and_articles = TPISector.find_by(slug: 'ascor')&.publications_and_articles || [] + ascor_page = TPIPage.find_by(slug: 'ascor') + @methodology_description = Content.find_by(page: ascor_page, code: 'methodology_description') + @methodology_id = Content.find_by(page: ascor_page, code: 'methodology_publication_id') + @methodology_publication = Publication.find_by(id: @methodology_id&.text) + + fixed_navbar('ASCOR Countries', admin_ascor_countries_path) + end + + def show + @assessment = ASCOR::Assessment.find_by country: @country, assessment_date: @assessment_date + @recent_emissions = Api::ASCOR::RecentEmissions.new(@assessment_date, @country).call + fixed_navbar("ASCOR Country #{@country.name}", admin_ascor_country_path(@country.id)) + end + + def index_assessment; end + + def index_emissions_assessment; end + + def show_assessment; end + + def emissions_chart_data + data = ::Api::ASCOR::EmissionsChart.new( + @emissions_assessment_date, + params[:emissions_metric], + params[:emissions_boundary], + params[:country_ids] + ).call + + render json: data + end + + def user_download + render zip: { + 'ASCOR_countries.xlsx' => Api::CSVToExcel.new(CSVExport::ASCOR::Countries.new.call).call, + 'ASCOR_indicators.xlsx' => Api::CSVToExcel.new(CSVExport::ASCOR::AssessmentIndicators.new.call).call, + 'ASCOR_assessments_results.xlsx' => Api::CSVToExcel.new(CSVExport::ASCOR::Assessments.new.call).call, + 'ASCOR_benchmarks.xlsx' => Api::CSVToExcel.new(CSVExport::ASCOR::Benchmarks.new.call).call, + 'ASCOR_assessments_results_trends_pathways.xlsx' => Api::CSVToExcel.new(CSVExport::ASCOR::Pathways.new.call).call + }, filename: "TPI ASCOR data - #{Time.now.strftime('%d%m%Y')}" + end + + private + + def fetch_ascor_countries + @countries = ASCOR::Country.all.order(:name) + @countries_json = [ + {name: 'All countries', path: tpi_ascor_index_path}, + *@countries.as_json(only: [:name], methods: [:path]) + ] + end + + def fetch_ascor_country + @country = ASCOR::Country.friendly.find(params[:id]) + end + + def fetch_assessment_date + @assessment_date = params[:assessment_date] || ASCOR::Assessment.maximum(:assessment_date) + end + + def fetch_emissions_assessment_date + @emissions_assessment_date = params[:emissions_assessment_date] || ASCOR::Assessment.maximum(:assessment_date) + end + + def fetch_ascor_assessment_results + @ascor_assessment_results = Api::ASCOR::BubbleChart.new(@assessment_date).call + end + end +end diff --git a/app/helpers/ascor_helper.rb b/app/helpers/ascor_helper.rb new file mode 100644 index 000000000..bc7c81810 --- /dev/null +++ b/app/helpers/ascor_helper.rb @@ -0,0 +1,16 @@ +module ASCORHelper + def ascor_icon_for(indicator, assessment) + value = ascor_assessment_result_for(indicator, assessment).answer.to_s.downcase.tr(' ', '-') + return 'no-data' unless value.in?(%w[yes no partial no-data no-disclosure not-applicable excempt]) + + value + end + + def ascor_sub_indicators_for(indicator, sub_indicators) + sub_indicators.select { |i| i.code.include? indicator.code } + end + + def ascor_assessment_result_for(indicator, assessment) + assessment.results.find { |r| r.indicator_id == indicator.id } + end +end diff --git a/app/javascript/components/tpi/AscorDropdown.js b/app/javascript/components/tpi/AscorDropdown.js new file mode 100644 index 000000000..98559bc04 --- /dev/null +++ b/app/javascript/components/tpi/AscorDropdown.js @@ -0,0 +1,179 @@ +import React, { + useState, + useMemo, + useRef, + useEffect, + useCallback, + Fragment +} from 'react'; +import PropTypes from 'prop-types'; +import Fuse from 'fuse.js'; +import cx from 'classnames'; +import chevronIcon from 'images/icons/white-chevron-down.svg'; +import chevronIconBlack from 'images/icon_chevron_dark/chevron_down_black-1.svg'; + +const ESCAPE_KEY = 27; +const ENTER_KEY = 13; + +const AscorSelector = ({ banks, selectedOption }) => { + const [searchValue, setSearchValue] = useState(''); + const [isOpen, setIsOpen] = useState(false); + const inputEl = useRef(null); + const searchContainer = useRef(null); + + const fuse = (opt) => { + const config = { + shouldSort: true, + threshold: 0.3, + keys: ['name'] + }; + const fuzzy = new Fuse(opt, config); + const searchResults = fuzzy.search(searchValue); + return searchResults; + }; + + const searchResults = useMemo(() => (searchValue ? fuse(banks) : []), [searchValue]); + + const options = useMemo(() => (searchValue + ? searchResults : banks), + [searchValue, banks]); + + const input = () => ( + setSearchValue(e.target.value)} + placeholder="Type or select bank" + /> + ); + + const header = () => ( + {selectedOption} + ); + + const handleOptionClick = (option) => { + setIsOpen(false); + window.location = option.path; + }; + + const handleCloseDropdown = () => { + setIsOpen(false); + setSearchValue(''); + }; + + const handleOpenSearch = () => { + if (!isOpen) setIsOpen(true); + }; + + // hooks + + useEffect(() => { + if (isOpen) { inputEl.current.focus(); } + }, [isOpen]); + + const escFunction = useCallback((event) => { + if (event.keyCode === ESCAPE_KEY) { + handleCloseDropdown(); + } + }, []); + + const enterFunction = (event) => { + if (event.keyCode === ENTER_KEY) { + if (isOpen && searchResults.length) { + handleOptionClick(searchResults[0]); + } + } + }; + + const handleClickOutside = useCallback((event) => { + if (searchContainer.current && !searchContainer.current.contains(event.target)) { + handleCloseDropdown(); + } + }, []); + + useEffect(() => { + document.addEventListener('keydown', escFunction, false); + + return () => { + document.removeEventListener('keydown', escFunction, false); + }; + }, []); + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + useEffect(() => { + document.addEventListener('keydown', enterFunction); + + return () => { + document.removeEventListener('keydown', enterFunction); + }; + }, [searchResults]); + + return ( + +
+
+
+ +
+
+ {isOpen ? input() : header()} + isOpen && handleCloseDropdown()} + className={cx('chevron-icon', { 'chevron-icon-rotated': isOpen })} + src={isOpen ? chevronIconBlack : chevronIcon} + alt="chevron" + /> +
+ {isOpen && ( +
+
+ {(options.length && options.map((option, i) => ( +
handleOptionClick(option)} + className="dropdown-selector__option" + key={`${option.name}-${i}`} + > + {option.name} +
+ ))) || (searchValue.length && !options.length && ( +
No results found.
+ ))} +
+
+ )} +
+
+
+ ); +}; + +AscorSelector.propTypes = { + banks: PropTypes.array.isRequired, + selectedOption: PropTypes.string.isRequired +}; + +export default AscorSelector; diff --git a/app/javascript/components/tpi/AscorQuestionLegend.js b/app/javascript/components/tpi/AscorQuestionLegend.js new file mode 100644 index 000000000..cfae07a62 --- /dev/null +++ b/app/javascript/components/tpi/AscorQuestionLegend.js @@ -0,0 +1,72 @@ +import React, { useState, useEffect } from 'react'; +import cx from 'classnames'; + +const AscorQuestionLegend = () => { + const [isVisible, setVisible] = useState(false); + + const isChecked = () => { + let anyChecked = false; + + document.querySelectorAll('.country-assessment > .country-assessment__pillar').forEach((section) => { + let areaChecked = false; + + section.querySelectorAll('input.toggle').forEach((input) => { + areaChecked = areaChecked || input.checked; + anyChecked = anyChecked || input.checked; + }); + return areaChecked ? section.classList.add('active') : section.classList.remove('active'); + }); + + setVisible(anyChecked); + }; + + useEffect(() => { + const eventListeners = []; + document.querySelectorAll('.country-assessment > .country-assessment__pillar > input.toggle').forEach((input) => { + const listener = input.addEventListener('click', isChecked); + eventListeners.push([input, listener]); + }); + + return () => { + eventListeners.forEach(([input, listener]) => { + input.removeEventListener('click', listener); + }); + }; + }); + + return ( +
+
+ Legend +
+
+
+ No +
+
+ Partial +
+
+ Yes +
+
+ No data +
+
+ Not applicable +
+
+ No disclosure +
+
+ Excempt +
+
+
+ ); +}; + +AscorQuestionLegend.propTypes = { +}; + +export default AscorQuestionLegend; diff --git a/app/javascript/components/tpi/AscorRecentEmissions.js b/app/javascript/components/tpi/AscorRecentEmissions.js new file mode 100644 index 000000000..3f809a73f --- /dev/null +++ b/app/javascript/components/tpi/AscorRecentEmissions.js @@ -0,0 +1,132 @@ +import PropTypes from 'prop-types'; +import Select from './Select'; +import React, {useState} from 'react'; + +const AscorRecentEmissions = ({ + emissions_metric_filter, + default_emissions_metric_filter, + emissions_boundary_filter, + default_emissions_boundary_filter, + trend_filters, + default_trend_filter, + data +}) => { + const [filters, setFilters] = useState({ + emissions_metric: default_emissions_metric_filter, + emissions_boundary: default_emissions_boundary_filter, + trends: default_trend_filter + }); + const recentEmissions = data.filter((d) => d.emissions_metric === filters.emissions_metric && d.emissions_boundary === filters.emissions_boundary)[0] || {}; + const trend = recentEmissions.trend || {}; + const trendValue = trend.values?.filter((t) => t.filter === filters.trends)[0] || {}; + + const handleSelect = (opt) => { + setFilters({ ...filters, [opt.name]: opt.value }); + }; + + return ( + <> +
+
+ i. What is the country's most recent emissions level? +
+ { recentEmissions.source && ( +
+ Source ({recentEmissions.year}) +
+ )} +
+
+
+ +
+
+ { recentEmissions.value !== null && ( + <> +
+
+ {recentEmissions.value} {recentEmissions.unit} +
+ + )} +
+
+
+ ii. What is the country's most recent emissions trend? +
+ { trend.source && ( +
+ Source ({trend.year}) +
+ )} +
+
+
+ setSearchValue(e.target.value)} + placeholder="Type or select" + /> + ) : ( + + {value || placeholder} + + )} + isOpen && handleCloseDropdown()} + className={cx('chevron-icon', { + 'chevron-icon-rotated': isOpen + })} + src={chevronIconBlack} + alt="chevron" + title={isOpen ? 'Close dropdown' : 'Open dropdown'} + /> +
+ +
  • +
    +
      + {(filteredOptions.length + && filteredOptions.map((option, i) => ( +
    • handleOptionClick(option)} + className={`selector__option ${ + option.value === value && 'selector__option--selected' + }`} + key={`${option.label}-${i}`} + > + {option.label} +
    • + ))) + || (searchValue.length && !_options.length && ( +
    • No results found.
    • + ))} +
    +
    +
  • + +
    + ); +}; + +Select.propTypes = { + options: PropTypes.oneOfType([ + PropTypes.arrayOf( + PropTypes.shape({ + value: PropTypes.string.isRequired, + label: PropTypes.string.isRequired + }) + ).isRequired, + PropTypes.arrayOf(PropTypes.string).isRequired + ]).isRequired, + onSelect: PropTypes.func.isRequired, + value: PropTypes.string, + allowSearch: PropTypes.bool, + placeholder: PropTypes.string, + label: PropTypes.string.isRequired, + name: PropTypes.string.isRequired +}; + +Select.defaultProps = { + value: '', + allowSearch: false, + placeholder: '' +}; + +export default Select; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/Chart.js b/app/javascript/components/tpi/charts/ascor-bubble/Chart.js new file mode 100644 index 000000000..c24b6a9ef --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/Chart.js @@ -0,0 +1,212 @@ +import React, { useEffect, useMemo } from 'react'; +import PropTypes from 'prop-types'; +import SingleCell from './SingleCell'; + +import { SCORE_RANGES, VALUES } from './constants'; +import { groupBy, keys, pickBy, values } from 'lodash'; + +import ChartMobile from './chart-mobile'; + +const DESKTOP_MIN_WIDTH = 992; + +const SCALE = 1.25; + +// radius of bubbles +const COMPANIES_MARKET_CAP_GROUPS = { + large: 10 * SCALE, + medium: 5 * SCALE, + small: 3 * SCALE +}; + +const SINGLE_CELL_SVG_WIDTH = 120; +const SINGLE_CELL_SVG_HEIGHT = 100; + +let tooltip = null; + +const BubbleChart = ({ results }) => { + const tooltipEl = ''; + useEffect(() => { + document.body.insertAdjacentHTML('beforeend', tooltipEl); + tooltip = document.getElementById('bubble-chart-tooltip'); + }, []); + const ranges = keys(SCORE_RANGES); + + const parsedData = useMemo( + () => values(values(groupBy(results, 'pillar'))).map((value) => ({ + pillar: value[0].pillar, + values: values(groupBy(value, 'area')).map((areaValues) => { + const vValues = pickBy( + groupBy(areaValues, 'result'), + (_value, key) => key in VALUES + ); + const v = { + ...VALUES, + ...vValues + }; + return { + area: areaValues[0].area, + values: values(v) + }; + }) + })), + [results] + ); + + const [isMobile, setIsMobile] = React.useState(true); + + const handleResize = () => { + if (window.innerWidth < DESKTOP_MIN_WIDTH) { + setIsMobile(true); + } else { + setIsMobile(false); + } + }; + + useEffect(() => { + if (typeof window !== 'undefined') { + handleResize(); + window.addEventListener('resize', handleResize); + } + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return ( +
    +
    +
    Pillar
    +
    Area
    +
    + {ranges.map((range) => ( +
    +
    {range}
    +
    + ))} +
    + {isMobile ? ( +
    + +
    + ) : ( +
    + +
    + )} +
    + ); +}; + +const ForceLayoutBubbleChart = (countriesBubbles, uniqueKey) => { + const handleBubbleClick = (country) => window.open(country.path, '_blank'); + + return ( + + ); +}; + +const getTooltipText = ({ tooltipContent }) => { + if (tooltipContent) { + return ` +
    ${tooltipContent.header}
    + `; + } + return ''; +}; + +const showTooltip = (node, u) => { + const bubble = u._groups[0][node.index]; + + tooltip.innerHTML = getTooltipText(node); + tooltip.removeAttribute('hidden'); + const bubbleBoundingRect = bubble.getBoundingClientRect(); + const topOffset = bubbleBoundingRect.top - tooltip.offsetHeight + window.scrollY; + const leftOffset = bubbleBoundingRect.left + + (bubbleBoundingRect.width - tooltip.offsetWidth) / 2 + + window.scrollX; + + tooltip.style.left = `${leftOffset}px`; + tooltip.style.top = `${topOffset}px`; +}; + +const hideTooltip = () => { + tooltip.setAttribute('hidden', true); +}; + +const ChartRows = ({ data }) => data?.map((pillar, pillarIndex) => { + const pillarName = pillar.pillar; + const pillarSpan = pillar.values.length; + const pillarAcronym = pillarName + .split(' ') + .map((word) => word[0]) + .join(''); + + return ( + <> +
    + + {pillarIndex + 1}. {pillarName} + +
    +
    + + {pillar.values.map(({ area, values: areaValues }, areaIndex) => ( + <> +
    + {pillarAcronym} {areaIndex + 1}. {area} +
    + {areaValues.map((areaValuesResult, i) => { + const countriesBubbles = areaValuesResult.map((result) => ({ + value: COMPANIES_MARKET_CAP_GROUPS[result.market_cap_group], + tooltipContent: { + header: result.country_name, + value: result.result + }, + path: result.country_path, + color: result.color, + result: result.result + })); + + // Remove special characters from the key to be able to use d3-select as it uses querySelector + const cleanKey = area.replace(/[^a-zA-Z\-_:.]/g, ''); + const uniqueKey = `${cleanKey}-${areaIndex}-${i}`; + return ( +
    + {ForceLayoutBubbleChart(countriesBubbles, uniqueKey)} +
    + ); + })} + + ))} + + ); +}); + +BubbleChart.propTypes = { + results: PropTypes.arrayOf( + PropTypes.shape({ + area: PropTypes.string.isRequired, + market_cap_group: PropTypes.string.isRequired, + country_id: PropTypes.number.isRequired, + country_path: PropTypes.string.isRequired, + country_name: PropTypes.string.isRequired, + result: PropTypes.string.isRequired, + pillar: PropTypes.string.isRequired + }) + ).isRequired +}; +export default BubbleChart; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/SingleCell.js b/app/javascript/components/tpi/charts/ascor-bubble/SingleCell.js new file mode 100644 index 000000000..ce1b2bf0a --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/SingleCell.js @@ -0,0 +1,91 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { range } from 'd3-array'; +import { select } from 'd3-selection'; +import * as d3 from 'd3-force'; +import { SCORE_RANGES } from './constants'; + +const SingleCell = ({ + width, + height, + handleNodeClick, + data, + uniqueKey, + showTooltip, + hideTooltip +}) => { + const computizedKey = uniqueKey.split(' ').join('_'); + const key = `${computizedKey.replace(/[&]/g, '_')}-${( + Math.random() * 100 + ).toFixed()}`; + + const nodes = range(data.length).map(function (index) { + return { + color: SCORE_RANGES[data[index].result], + tooltipContent: data[index].tooltipContent, + path: data[index].path, + radius: data[index].value, + value: data[index].result + }; + }); + + const simulation = () => { + d3.forceSimulation(nodes) + .force('charge', d3.forceManyBody().strength(10)) + .force('y', d3.forceY().strength(0.3).y(0)) + .force( + 'collision', + d3.forceCollide().radius(function (d) { + return d.radius + 1; + }) + ) + .on('tick', ticked); + }; + + const ticked = () => { + const u = select(`#${key}`).select('g').selectAll('circle').data(nodes); + + u.enter() + .append('circle') + .attr('r', (d) => d.radius) + .style('fill', (d) => d.color) + .merge(u) + .attr('cx', (d) => d.x) + .attr('cy', (d) => d.y) + .on('mouseover', (d) => showTooltip(d, u)) + .on('mouseout', hideTooltip) + .on('click', (d) => handleNodeClick(d)); + + u.exit().remove(); + }; + + simulation(); + + return ( + + + + + + ); +}; + +SingleCell.propTypes = { + showTooltip: PropTypes.func.isRequired, + hideTooltip: PropTypes.func.isRequired, + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + handleNodeClick: PropTypes.func.isRequired, + data: PropTypes.oneOfType([PropTypes.number, PropTypes.array]).isRequired, + uniqueKey: PropTypes.string.isRequired +}; + +export default SingleCell; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/ChartMobile.js b/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/ChartMobile.js new file mode 100644 index 000000000..d663829cd --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/ChartMobile.js @@ -0,0 +1,172 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import chevronIcon from 'images/icons/white-chevron-down.svg'; +import chevronIconBlack from 'images/icon_chevron_dark/chevron_down_black-1.svg'; +import { SCORE_RANGES } from '../constants'; + +const Item = ({ title, children, className, isOpen, onOpen, icon }) => ( +
  • +
    + {title} + chevron +
    +
    + {children} +
    +
  • +); + +Item.propTypes = { + title: PropTypes.string.isRequired, + className: PropTypes.string, + children: PropTypes.node, + onOpen: PropTypes.func.isRequired, + isOpen: PropTypes.bool, + icon: PropTypes.string.isRequired +}; +Item.defaultProps = { + className: '', + children: null, + isOpen: false +}; + +const ResultItem = ({ result, i }) => { + const [isOpen, setIsOpen] = useState(false); + { + const color = Object.values(SCORE_RANGES)[i]; + const title = `${result.length} countries`; + + return ( +
  • +
    setIsOpen((prevState) => !prevState)} + > +
    + {!isOpen && {title}} +
    + + {isOpen && ( +
      + {result.length ? ( + result.map((country) => ( +
    • {country.country_name}
    • + )) + ) : ( +
    • No countries
    • + )} +
    + )} +
  • + ); + } +}; + +const ChartMobile = ({ data }) => { + const [openPillars, setOpenPillars] = useState([]); + const [openAreas, setOpenAreas] = useState([]); + + const handleOpenAreas = (key) => { + setOpenAreas((prevState) => { + if (prevState.includes(key)) { + return prevState.filter((area) => area !== key); + } + return [...prevState, key]; + }); + }; + + const handleOpenPillars = (key) => { + if (openPillars.includes(key)) { + setOpenPillars((prevState) => prevState.filter((pillar) => pillar !== key)); + setOpenAreas((prevState) => prevState.filter((area) => !area.includes(`${key}.`))); + return; + } + setOpenPillars((prevState) => [...prevState, key]); + }; + + return ( +
    +
      + {data.map((pillar) => ( + handleOpenPillars(pillar.pillar)} + icon={chevronIcon} + > +
        + {pillar.values.map((area, areaIndex) => { + const pillarAcronym = pillar.pillar + .split(' ') + .map((word) => word[0]) + .join(''); + const title = `${pillarAcronym} ${areaIndex + 1}. ${area.area}`; + return ( + handleOpenAreas(`${pillar.pillar}.${area.area}`)} + icon={chevronIconBlack} + > +
          + {area.values.map((result, i) => ( + + ))} +
        +
        + ); + })} +
      +
      + ))} +
    +
    + ); +}; + +ChartMobile.propTypes = { + data: PropTypes.arrayOf( + PropTypes.shape({ + pillar: PropTypes.string, + values: PropTypes.arrayOf( + PropTypes.shape({ + area: PropTypes.string, + values: PropTypes.array + }) + ) + }) + ) +}; + +ChartMobile.defaultProps = { + data: [] +}; + +export default ChartMobile; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/index.js b/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/index.js new file mode 100644 index 000000000..dc8cbf4b0 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/chart-mobile/index.js @@ -0,0 +1,3 @@ +import ChartMobile from './ChartMobile'; + +export default ChartMobile; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/constants.js b/app/javascript/components/tpi/charts/ascor-bubble/constants.js new file mode 100644 index 000000000..f31d301d6 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/constants.js @@ -0,0 +1,7 @@ +export const SCORE_RANGES = { + No: '#F26E6E', + Partial: '#F9A400', + Yes: '#17B091' +}; + +export const VALUES = { No: [], Partial: [], Yes: [] }; diff --git a/app/javascript/components/tpi/charts/ascor-bubble/index.js b/app/javascript/components/tpi/charts/ascor-bubble/index.js new file mode 100644 index 000000000..9ec02597f --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-bubble/index.js @@ -0,0 +1,3 @@ +import Chart from './Chart'; + +export { Chart }; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/Chart.js b/app/javascript/components/tpi/charts/ascor-emissions/Chart.js new file mode 100644 index 000000000..21ff57afb --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/Chart.js @@ -0,0 +1,56 @@ +import PropTypes from 'prop-types'; + +import React from 'react'; + +import Highcharts from 'highcharts'; +import HighchartsReact from 'highcharts-react-official'; + +import { options } from './options'; + +const EmissionsChart = ({ chartData }) => { + const { data, metadata } = chartData; + + return ( +
    + +
    + ); +}; + +export default EmissionsChart; + +EmissionsChart.propTypes = { + chartData: PropTypes.shape({ + data: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + data: PropTypes.arrayOf(PropTypes.object).isRequired, + zoneAxis: PropTypes.string, + zones: PropTypes.arrayOf(PropTypes.object) + }) + ), + metadata: PropTypes.shape({ + unit: PropTypes.string.isRequired + }) + }) +}; + +EmissionsChart.defaultProps = { + chartData: { + data: [], + metadata: { + unit: '' + } + } +}; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/CountrySelector.js b/app/javascript/components/tpi/charts/ascor-emissions/CountrySelector.js new file mode 100644 index 000000000..124bf9ea8 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/CountrySelector.js @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; + +import PropTypes from 'prop-types'; + +const CountrySelector = ({ + countries, + selectedCountries: defaultSelectedCountries, + maxSelectedCountries, + onSaveCountries +}) => { + const [countriesOpen, setCountriesOpen] = useState(false); + const [selectedCountries, setSelectedCountries] = useState( + defaultSelectedCountries + ); + + const _countries = countries.sort((a, b) => (a.name < b.name ? -1 : 1)); + + const handleSelectedCountry = (event) => { + const { checked, value } = event.target; + + if (checked && selectedCountries.length >= maxSelectedCountries) { + return; + } + + const parsedValue = parseInt(value, 10); + + if (checked) { + setSelectedCountries([...selectedCountries, parsedValue]); + } else { + setSelectedCountries( + selectedCountries.filter((id) => id !== parsedValue) + ); + } + }; + + const handleSaveCountries = () => { + setCountriesOpen(false); + onSaveCountries(selectedCountries); + }; + + return ( +
    + +
    +
    +

    + Add up to 10 countries simultaneously +

    +
      + {_countries.map((_country) => ( +
    • + + +
    • + ))} +
    +
    + +
    +
    +
    +
    + ); +}; + +CountrySelector.propTypes = { + countries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + iso: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }) + ).isRequired, + selectedCountries: PropTypes.arrayOf(PropTypes.number), + maxSelectedCountries: PropTypes.number.isRequired, + onSaveCountries: PropTypes.func.isRequired +}; + +CountrySelector.defaultProps = { + selectedCountries: [] +}; + +export default CountrySelector; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/Emissions.js b/app/javascript/components/tpi/charts/ascor-emissions/Emissions.js new file mode 100644 index 000000000..a3304fc98 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/Emissions.js @@ -0,0 +1,96 @@ +import React, { useMemo, useState } from 'react'; + +import PropTypes from 'prop-types'; +import Filters from './Filters'; +import EmissionsChart from './Chart'; +import { useChartData } from '../hooks'; + +const initialData = { + data: [], + metadata: { + unit: '' + } +}; + +const Emissions = ({ + emissions_metric_filter, + default_emissions_metric_filter, + emissions_boundary_filter, + default_emissions_boundary_filter, + countries, + default_countries, + emissions_data_url +}) => { + const [filters, setFilters] = useState({ + emissions_metric: default_emissions_metric_filter, + emissions_boundary: default_emissions_boundary_filter, + country_ids: default_countries + }); + + const onChangeFilters = (filter) => { + setFilters((_filters) => ({ ..._filters, ...filter })); + }; + + const { data } = useChartData(emissions_data_url, filters); + + const chartData = useMemo( + () => (Array.isArray(data) + ? initialData + : { + ...data, + data: Object.entries(data.data).map( + ([countryId, { emissions, last_historical_year }]) => ({ + name: countries.find( + (country) => country.id === Number(countryId) + ).name, + custom: { unit: data.metadata.unit }, + data: Object.entries(emissions).map(([year, value]) => ({ + x: Number(year), + y: value + })), + zoneAxis: 'x', + zones: [ + { + value: last_historical_year + }, + { + dashStyle: 'dash' + } + ] + }) + ) + }), + [countries, data] + ); + + return ( +
    + + +
    + ); +}; + +Emissions.propTypes = { + emissions_metric_filter: PropTypes.arrayOf(PropTypes.string).isRequired, + default_emissions_metric_filter: PropTypes.string.isRequired, + emissions_boundary_filter: PropTypes.arrayOf(PropTypes.string).isRequired, + default_emissions_boundary_filter: PropTypes.string.isRequired, + countries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + iso: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }) + ).isRequired, + default_countries: PropTypes.arrayOf(PropTypes.number).isRequired, + emissions_data_url: PropTypes.string.isRequired +}; + +export default Emissions; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/Filters.js b/app/javascript/components/tpi/charts/ascor-emissions/Filters.js new file mode 100644 index 000000000..d3db7a4f8 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/Filters.js @@ -0,0 +1,73 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Select from '../../Select'; +import CountrySelector from './CountrySelector'; + +const MAX_SELECTED_COUNTRIES = 10; + +const Filters = ({ + metrics, + boundaries, + countries, + filters: { emissions_metric, emissions_boundary, country_ids }, + onChangeFilters +}) => { + const handleSelect = (opt) => { + onChangeFilters({ [opt.name]: opt.value }); + }; + + const handleSelectCountry = (_countries) => { + onChangeFilters({ country_ids: _countries }); + }; + + return ( +
    +
    + +
    +
    + +
    +
    + ); +}; + +Filters.propTypes = { + metrics: PropTypes.arrayOf(PropTypes.string).isRequired, + boundaries: PropTypes.arrayOf(PropTypes.string).isRequired, + countries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + iso: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }) + ).isRequired, + filters: PropTypes.shape({ + emissions_metric: PropTypes.string.isRequired, + emissions_boundary: PropTypes.string.isRequired, + country_ids: PropTypes.arrayOf(PropTypes.number).isRequired + }).isRequired, + onChangeFilters: PropTypes.func.isRequired +}; + +export default Filters; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/index.js b/app/javascript/components/tpi/charts/ascor-emissions/index.js new file mode 100644 index 000000000..0c0b97a9d --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/index.js @@ -0,0 +1,3 @@ +import Emissions from './Emissions'; + +export default Emissions; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/mockedData.js b/app/javascript/components/tpi/charts/ascor-emissions/mockedData.js new file mode 100644 index 000000000..86655b1b9 --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/mockedData.js @@ -0,0 +1,224 @@ +export default { + data: { + 1: { + emissions: { + 2005: 22, + 2006: 22, + 2007: 22, + 2008: 22, + 2009: 20, + 2010: 20, + 2011: 19, + 2012: 19, + 2013: 20, + 2014: 19, + 2015: 18, + 2016: 17, + 2017: 17, + 2018: 17, + 2019: 17, + 2020: 15, + 2021: 15, + 2022: 14, + 2023: 15, + 2024: 15, + 2025: 15, + 2026: 15, + 2027: 13, + 2028: 13, + 2029: 13, + 2030: 13 + }, + last_historical_year: 2020 + }, + 2: { + emissions: { + 2005: 12, + 2006: 12, + 2007: 10, + 2008: 10, + 2009: 10, + 2010: 10, + 2011: 10, + 2012: 10, + 2013: 10, + 2014: 9, + 2015: 9, + 2016: 9, + 2017: 9, + 2018: 9, + 2019: 9, + 2020: 9, + 2021: 9, + 2022: 9, + 2023: 9, + 2024: 9, + 2025: 7, + 2026: 7, + 2027: 7, + 2028: 7, + 2029: 7, + 2030: 7 + }, + last_historical_year: 2020 + }, + 3: { + emissions: { + 2005: 22, + 2006: 22, + 2007: 22, + 2008: 22, + 2009: 22, + 2010: 20, + 2011: 19, + 2012: 19, + 2013: 20, + 2014: 20, + 2015: 18, + 2016: 17, + 2017: 17, + 2018: 17, + 2019: 17, + 2020: 17, + 2021: 16, + 2022: 16, + 2023: 16, + 2024: 16, + 2025: 16, + 2026: 15, + 2027: 15, + 2028: 15, + 2029: 15, + 2030: 15 + }, + last_historical_year: 2020 + }, + 4: { + emissions: { + 2005: 10, + 2006: 12, + 2007: 14, + 2008: 16, + 2009: 18, + 2010: 20, + 2011: 19, + 2012: 19, + 2013: 20, + 2014: 19, + 2015: 18, + 2016: 17, + 2017: 17, + 2018: 16, + 2019: 16, + 2020: 15, + 2021: 15, + 2022: 14, + 2023: 14, + 2024: 14, + 2025: 14, + 2026: 13, + 2027: 13, + 2028: 13, + 2029: 13, + 2030: 13 + }, + last_historical_year: 2020 + }, + 14: { + emissions: { + 2005: 10, + 2006: 12, + 2007: 14, + 2008: 16, + 2009: 18, + 2010: 20, + 2011: 19, + 2012: 19, + 2013: 20, + 2014: 19, + 2015: 18, + 2016: 17, + 2017: 17, + 2018: 16, + 2019: 16, + 2020: 15, + 2021: 15, + 2022: 14, + 2023: 14, + 2024: 14, + 2025: 14, + 2026: 13, + 2027: 13, + 2028: 13, + 2029: 13, + 2030: 13 + }, + last_historical_year: 2020 + }, + 21: { + emissions: { + 2005: 10, + 2006: 12, + 2007: 14, + 2008: 16, + 2009: 18, + 2010: 20, + 2011: 19, + 2012: 19, + 2013: 20, + 2014: 19, + 2015: 18, + 2016: 17, + 2017: 17, + 2018: 16, + 2019: 16, + 2020: 15, + 2021: 15, + 2022: 14, + 2023: 14, + 2024: 14, + 2025: 14, + 2026: 13, + 2027: 13, + 2028: 13, + 2029: 13, + 2030: 13 + }, + last_historical_year: 2020 + }, + 22: { + emissions: { + 2005: 13, + 2006: 12, + 2007: 11, + 2008: 12, + 2009: 13, + 2010: 11, + 2011: 14, + 2012: 14, + 2013: 12, + 2014: 13, + 2015: 13, + 2016: 12, + 2017: 12, + 2018: 11, + 2019: 11, + 2020: 11, + 2021: 10, + 2022: 9, + 2023: 9, + 2024: 9, + 2025: 8, + 2026: 8, + 2027: 7, + 2028: 7, + 2029: 8, + 2030: 7 + }, + last_historical_year: 2020 + } + }, + metadata: { + unit: 'MtCO2e' + } +}; diff --git a/app/javascript/components/tpi/charts/ascor-emissions/options.js b/app/javascript/components/tpi/charts/ascor-emissions/options.js new file mode 100644 index 000000000..9f2ac1acc --- /dev/null +++ b/app/javascript/components/tpi/charts/ascor-emissions/options.js @@ -0,0 +1,167 @@ +export const colors = [ + '#17B091', + '#F26E6E', + '#5454C4', + '#B75038', + '#FFDD49', + '#00A8FF', + '#F602B4', + '#191919' +]; + +const tooltipLegendLine = ( + dashStyle, + color +) => ` +${ + dashStyle === 'dash' + ? `` + : `` +} +`; + +export const options = { + title: { text: '' }, + colors, + yAxis: { + lineColor: '#595B5D', + lineWidth: 1, + visible: true, + tickColor: '#595B5D', + tickAmount: 8, + labels: { + padding: 10, + style: { + color: '#595B5D', + fontSize: '12px' + } + }, + title: { + useHTML: true, + align: 'high' + } + }, + credits: { + enabled: false + }, + xAxis: { + lineColor: '#595B5D', + lineWidth: 1, + tickColor: '#595B5D', + tickInterval: 5, + labels: { + style: { + color: '#0A4BDC', + fontSize: '14px' + }, + overflow: 'allow' + } + }, + legend: { + layout: 'horizontal', + align: 'left', + verticalAlign: 'bottom', + padding: 50, + itemDistance: 12, + symbolHeight: 0, + symbolWidth: 0, + useHTML: true, + itemMarginBottom: 10, + labelFormatter() { + return `
    ${this.name}
    `; + }, + className: 'emissions__chart__legend' + }, + tooltip: { + shared: true, + headerFormat: `
    + {point.key} {series.userOptions.custom.unit} / country +
    `, + pointFormatter() { + return `
    +
    + ${tooltipLegendLine(this.zone.dashStyle, this.color)} + ${this.series.name}${ + this.zone.dashStyle === 'dash' + ? ' (target)' + : '' +} +
    + ${this.y} +
    `; + }, + style: { + color: '#191919', + fontSize: '14px' + }, + borderWidth: 0, + crosshairs: true, + padding: 0, + shadow: false, + className: 'emissions__chart__tooltip', + useHTML: true + }, + chart: { + height: 550, + showAxes: true + }, + plotOptions: { + series: { + marker: { + enabled: false, + states: { + hover: { + enabled: false + } + } + } + } + }, + series: [], + responsive: { + rules: [ + { + condition: { + maxWidth: 992 + }, + chartOptions: { + legend: { + align: 'left', + padding: 0, + itemDistance: 10, + alignColumns: false, + margin: 0, + itemMarginTop: 0 + }, + yAxis: { + labels: { + align: 'center', + distance: 5, + padding: 0, + style: { + fontSize: '10px' + } + }, + title: { + reserveSpace: false, + rotation: 0, + style: { + fontSize: '10px' + } + } + }, + xAxis: { + labels: { + style: { + fontSize: '10px' + } + } + }, + chart: { + height: 350 + } + } + } + ] + } +}; diff --git a/app/javascript/components/tpi/charts/hooks.js b/app/javascript/components/tpi/charts/hooks.js index 199836d61..8e57f5965 100644 --- a/app/javascript/components/tpi/charts/hooks.js +++ b/app/javascript/components/tpi/charts/hooks.js @@ -1,12 +1,17 @@ +import { isEmpty } from 'lodash'; import { useEffect, useState } from 'react'; -export function useChartData(dataUrl) { +export function useChartData(dataUrl, params = {}) { const [data, setData] = useState([]); const [error, setError] = useState(''); const [loading, setLoading] = useState(true); + const url = isEmpty(params) + ? dataUrl + : `${dataUrl}?${new URLSearchParams(params)}`; + useEffect(() => { - fetch(dataUrl) + fetch(url) .then((r) => r.json()) .then((chartData) => { setLoading(false); @@ -16,7 +21,7 @@ export function useChartData(dataUrl) { setLoading(false); setError('Error while loading the data'); }); - }, [dataUrl]); + }, [url]); return { data, diff --git a/app/models/ascor.rb b/app/models/ascor.rb new file mode 100644 index 000000000..6ecbd743b --- /dev/null +++ b/app/models/ascor.rb @@ -0,0 +1,5 @@ +module ASCOR + def self.table_name_prefix + 'ascor_' + end +end diff --git a/app/models/ascor/assessment.rb b/app/models/ascor/assessment.rb new file mode 100644 index 000000000..743dd5dea --- /dev/null +++ b/app/models/ascor/assessment.rb @@ -0,0 +1,22 @@ +# == Schema Information +# +# Table name: ascor_assessments +# +# id :bigint not null, primary key +# country_id :bigint not null +# assessment_date :date +# publication_date :date +# created_at :datetime not null +# updated_at :datetime not null +# notes :text +# +class ASCOR::Assessment < ApplicationRecord + belongs_to :country, class_name: 'ASCOR::Country', foreign_key: :country_id + + has_many :results, class_name: 'ASCOR::AssessmentResult', foreign_key: :assessment_id, dependent: :destroy, + inverse_of: :assessment + + validates_presence_of :assessment_date + + accepts_nested_attributes_for :results, allow_destroy: true +end diff --git a/app/models/ascor/assessment_indicator.rb b/app/models/ascor/assessment_indicator.rb new file mode 100644 index 000000000..909b58642 --- /dev/null +++ b/app/models/ascor/assessment_indicator.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: ascor_assessment_indicators +# +# id :bigint not null, primary key +# indicator_type :string +# code :string +# text :text +# created_at :datetime not null +# updated_at :datetime not null +# units_or_response_type :string +# +class ASCOR::AssessmentIndicator < ApplicationRecord + INDICATOR_TYPES = %w[pillar area indicator metric].freeze + enum indicator_type: array_to_enum_hash(INDICATOR_TYPES) + + has_many :results, class_name: 'ASCOR::AssessmentResult', foreign_key: :indicator_id, dependent: :destroy + + validates_presence_of :indicator_type, :code, :text + + def to_s + "#{indicator_type} #{code}" + end +end diff --git a/app/models/ascor/assessment_result.rb b/app/models/ascor/assessment_result.rb new file mode 100644 index 000000000..3275d559f --- /dev/null +++ b/app/models/ascor/assessment_result.rb @@ -0,0 +1,22 @@ +# == Schema Information +# +# Table name: ascor_assessment_results +# +# id :bigint not null, primary key +# assessment_id :bigint not null +# indicator_id :bigint not null +# answer :string +# created_at :datetime not null +# updated_at :datetime not null +# source :string +# year :integer +# +class ASCOR::AssessmentResult < ApplicationRecord + belongs_to :assessment, class_name: 'ASCOR::Assessment', foreign_key: :assessment_id + belongs_to :indicator, class_name: 'ASCOR::AssessmentIndicator', foreign_key: :indicator_id + + validates_uniqueness_of :indicator_id, scope: :assessment_id + + scope :of_type, ->(type) { includes(:indicator).where(ascor_assessment_indicators: {indicator_type: type}) } + scope :by_date, ->(date) { includes(:assessment).where(ascor_assessments: {assessment_date: date}) } +end diff --git a/app/models/ascor/benchmark.rb b/app/models/ascor/benchmark.rb new file mode 100644 index 000000000..922d72003 --- /dev/null +++ b/app/models/ascor/benchmark.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: ascor_benchmarks +# +# id :bigint not null, primary key +# country_id :bigint not null +# publication_date :date +# emissions_metric :string +# emissions_boundary :string +# units :string +# benchmark_type :string +# emissions :jsonb +# created_at :datetime not null +# updated_at :datetime not null +# +class ASCOR::Benchmark < ApplicationRecord + include HasEmissions + + belongs_to :country, class_name: 'ASCOR::Country', foreign_key: :country_id + + validates_presence_of :emissions_metric, :emissions_boundary, :units, :benchmark_type + validates :emissions_metric, inclusion: {in: ASCOR::EmissionsMetric::VALUES}, allow_nil: true + validates :emissions_boundary, inclusion: {in: ASCOR::EmissionsBoundary::VALUES}, allow_nil: true + validates :benchmark_type, inclusion: {in: ASCOR::BenchmarkType::VALUES}, allow_nil: true +end diff --git a/app/models/ascor/benchmark_type.rb b/app/models/ascor/benchmark_type.rb new file mode 100644 index 000000000..d7e124829 --- /dev/null +++ b/app/models/ascor/benchmark_type.rb @@ -0,0 +1,6 @@ +class ASCOR::BenchmarkType + VALUES = [ + 'National 1.5C benchmark', + 'Fair share 1.5C allocation' + ].freeze +end diff --git a/app/models/ascor/country.rb b/app/models/ascor/country.rb new file mode 100644 index 000000000..1c4ae3f5d --- /dev/null +++ b/app/models/ascor/country.rb @@ -0,0 +1,59 @@ +# == Schema Information +# +# Table name: ascor_countries +# +# id :bigint not null, primary key +# name :string +# slug :string +# iso :string +# region :string +# wb_lending_group :string +# fiscal_monitor_category :string +# created_at :datetime not null +# updated_at :datetime not null +# type_of_party :string +# +class ASCOR::Country < ApplicationRecord + extend FriendlyId + + REGIONS = [ + 'Africa', + 'Asia', + 'Europe', + 'Latin America and Caribbean', + 'North America', + 'Oceania' + ].freeze + LENDING_GROUPS = [ + 'High-income economies', + 'Upper-middle-income economies', + 'Lower-middle-income economies' + ].freeze + MONITOR_CATEGORIES = [ + 'Advanced economies', + 'Emerging market economies', + 'Low-income developing countries' + ].freeze + TYPE_OF_PARTY = [ + 'Annex I', + 'Non-Annex I' + ].freeze + DEFAULT_COUNTRIES = %w[USA CAN GBR FRA DEU ITA JPN RUS].freeze + + friendly_id :name, use: [:slugged, :history], routes: :default + + has_many :benchmarks, class_name: 'ASCOR::Benchmark', foreign_key: :country_id, dependent: :destroy + has_many :pathways, class_name: 'ASCOR::Pathway', foreign_key: :country_id, dependent: :destroy + has_many :assessments, class_name: 'ASCOR::Assessment', foreign_key: :country_id, dependent: :destroy + + validates_presence_of :name, :slug, :iso, :region, :wb_lending_group, :fiscal_monitor_category + validates_uniqueness_of :name, :slug, :iso + validates :region, inclusion: {in: REGIONS}, allow_nil: true + validates :wb_lending_group, inclusion: {in: LENDING_GROUPS}, allow_nil: true + validates :fiscal_monitor_category, inclusion: {in: MONITOR_CATEGORIES}, allow_nil: true + validates :type_of_party, inclusion: {in: TYPE_OF_PARTY}, allow_nil: true + + def path + Rails.application.routes.url_helpers.tpi_ascor_path slug + end +end diff --git a/app/models/ascor/emissions_boundary.rb b/app/models/ascor/emissions_boundary.rb new file mode 100644 index 000000000..fc2d5e362 --- /dev/null +++ b/app/models/ascor/emissions_boundary.rb @@ -0,0 +1,7 @@ +class ASCOR::EmissionsBoundary + VALUES = [ + 'Production - excluding LULUCF', + 'Production - only LULUCF', + 'Consumption - excluding LULUCF' + ].freeze +end diff --git a/app/models/ascor/emissions_metric.rb b/app/models/ascor/emissions_metric.rb new file mode 100644 index 000000000..1dfe670c7 --- /dev/null +++ b/app/models/ascor/emissions_metric.rb @@ -0,0 +1,7 @@ +class ASCOR::EmissionsMetric + VALUES = [ + 'Absolute', + 'Intensity per capita', + 'Intensity per GDP-PPP' + ].freeze +end diff --git a/app/models/ascor/pathway.rb b/app/models/ascor/pathway.rb new file mode 100644 index 000000000..e85b56d15 --- /dev/null +++ b/app/models/ascor/pathway.rb @@ -0,0 +1,33 @@ +# == Schema Information +# +# Table name: ascor_pathways +# +# id :bigint not null, primary key +# country_id :bigint not null +# emissions_metric :string +# emissions_boundary :string +# units :string +# assessment_date :date +# publication_date :date +# last_historical_year :integer +# trend_1_year :string +# trend_3_year :string +# trend_5_year :string +# emissions :jsonb +# created_at :datetime not null +# updated_at :datetime not null +# trend_source :string +# trend_year :integer +# recent_emission_level :float +# recent_emission_source :string +# recent_emission_year :integer +# +class ASCOR::Pathway < ApplicationRecord + include HasEmissions + + belongs_to :country, class_name: 'ASCOR::Country', foreign_key: :country_id + + validates_presence_of :emissions_metric, :emissions_boundary, :units, :assessment_date + validates :emissions_metric, inclusion: {in: ASCOR::EmissionsMetric::VALUES}, allow_nil: true + validates :emissions_boundary, inclusion: {in: ASCOR::EmissionsBoundary::VALUES}, allow_nil: true +end diff --git a/app/models/company.rb b/app/models/company.rb index c3a0f0709..7c710fcc4 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -75,6 +75,10 @@ def latest_mq_assessment latest_mq_assessment_only_beta_methodologies || latest_mq_assessment_without_beta_methodologies end + def beta_mq_assessments? + mq_assessments.only_beta_methodologies.exists? + end + def should_generate_new_friendly_id? name_changed? || super end diff --git a/app/models/data_upload.rb b/app/models/data_upload.rb index 0e107c16d..9fda519d1 100644 --- a/app/models/data_upload.rb +++ b/app/models/data_upload.rb @@ -15,6 +15,11 @@ class DataUpload < ApplicationRecord DEV_UPLOADERS = %w[Documents].freeze UPLOADERS = { + 'ASCOR Countries' => 'ASCORCountries', + 'ASCOR Benchmarks' => 'ASCORBenchmarks', + 'ASCOR Pathways' => 'ASCORPathways', + 'ASCOR Assessment Indicators' => 'ASCORAssessmentIndicators', + 'ASCOR Assessments' => 'ASCORAssessments', 'Banks' => 'Banks', 'Bank Assessment Indicators' => 'BankAssessmentIndicators', 'Bank Assessments' => 'BankAssessments', diff --git a/app/services/api/ascor/bubble_chart.rb b/app/services/api/ascor/bubble_chart.rb new file mode 100644 index 000000000..dfae7d345 --- /dev/null +++ b/app/services/api/ascor/bubble_chart.rb @@ -0,0 +1,64 @@ +module Api + module ASCOR + class BubbleChart + MARKET_CAP_QUERY = { + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Production - excluding LULUCF' + }.freeze + + attr_accessor :assessment_date + + def initialize(assessment_date) + @assessment_date = assessment_date + end + + def call + ::ASCOR::AssessmentResult + .by_date(@assessment_date) + .of_type(:area) + .includes(assessment: :country) + .order(:indicator_id) + .map do |result| + { + pillar: pillars[result.indicator.code.split('.').first]&.first&.text, + area: result.indicator.text, + result: result.answer, + country_id: result.assessment.country_id, + country_name: result.assessment.country.name, + country_path: result.assessment.country.path, + market_cap_group: calculate_market_cap_group(result.assessment.country_id) + } + end + end + + private + + def calculate_market_cap_group(country_id) + recent_emission_level = recent_emission_levels[country_id]&.first&.recent_emission_level + return :medium if recent_emission_level.blank? + + market_cap_groups.find { |range, _| range.include?(recent_emission_level) }&.last || :medium + end + + def pillars + @pillars ||= ::ASCOR::AssessmentIndicator.where(indicator_type: :pillar).group_by(&:code) + end + + def recent_emission_levels + @recent_emission_levels ||= ::ASCOR::Pathway.where(MARKET_CAP_QUERY).where(assessment_date: assessment_date) + .select(:country_id, :recent_emission_level) + .group_by(&:country_id) + end + + def market_cap_groups + @market_cap_groups ||= begin + values = ::ASCOR::Pathway.where(MARKET_CAP_QUERY).where(assessment_date: assessment_date) + .where.not(recent_emission_level: nil).pluck(:recent_emission_level).sort + {values.first..values[(values.size * 1.0 / 3).ceil - 1] => :small, + values[(values.size * 1.0 / 3).ceil - 1]..values[(values.size * 2.0 / 3).ceil - 1] => :medium, + values[(values.size * 2.0 / 3).ceil - 1]..values.last => :large} + end + end + end + end +end diff --git a/app/services/api/ascor/emissions_chart.rb b/app/services/api/ascor/emissions_chart.rb new file mode 100644 index 000000000..f502ab86e --- /dev/null +++ b/app/services/api/ascor/emissions_chart.rb @@ -0,0 +1,51 @@ +module Api + module ASCOR + class EmissionsChart + attr_accessor :assessment_date, :emissions_metric, :emissions_boundary, :country_ids + + def initialize(assessment_date, emissions_metric, emissions_boundary, country_ids) + @assessment_date = assessment_date + @emissions_metric = emissions_metric || 'Absolute' + @emissions_boundary = emissions_boundary || 'Production - excluding LULUCF' + @country_ids = country_ids.is_a?(String) ? country_ids.split(',') : Array.wrap(country_ids) + end + + def call + {data: collect_data, metadata: collect_metadata} + end + + private + + def collect_data + countries.each_with_object({}) do |country, result| + pathway = pathways[country.id]&.first + result[country.id] = { + emissions: pathway&.emissions || {}, + last_historical_year: pathway&.last_historical_year + } + end + end + + def collect_metadata + {unit: pathways.values.flatten.first&.units} + end + + def countries + @countries ||= if country_ids.blank? + ::ASCOR::Country.where(iso: ::ASCOR::Country::DEFAULT_COUNTRIES) + else + ::ASCOR::Country.where(id: country_ids) + end + end + + def pathways + @pathways ||= ::ASCOR::Pathway.where( + country: countries, + emissions_metric: emissions_metric, + emissions_boundary: emissions_boundary, + assessment_date: assessment_date + ).group_by(&:country_id) + end + end + end +end diff --git a/app/services/api/ascor/recent_emissions.rb b/app/services/api/ascor/recent_emissions.rb new file mode 100644 index 000000000..fc6a60ba0 --- /dev/null +++ b/app/services/api/ascor/recent_emissions.rb @@ -0,0 +1,40 @@ +module Api + module ASCOR + class RecentEmissions + attr_accessor :assessment_date, :country + + def initialize(assessment_date, country) + @assessment_date = assessment_date + @country = country + end + + def call + pathways.map do |pathway| + { + value: pathway.recent_emission_level, + source: pathway.recent_emission_source, + year: pathway.recent_emission_year, + emissions_metric: pathway.emissions_metric, + emissions_boundary: pathway.emissions_boundary, + unit: pathway.units, + trend: { + source: pathway.trend_source, + year: pathway.trend_year, + values: [ + {filter: '1 year trend', value: pathway.trend_1_year}, + {filter: '3 years trend', value: pathway.trend_3_year}, + {filter: '5 years trend', value: pathway.trend_5_year} + ] + } + } + end + end + + private + + def pathways + @pathways ||= ::ASCOR::Pathway.where(assessment_date: assessment_date, country: country) + end + end + end +end diff --git a/app/services/csv_export/ascor/assessment_indicators.rb b/app/services/csv_export/ascor/assessment_indicators.rb new file mode 100644 index 000000000..f93559ea4 --- /dev/null +++ b/app/services/csv_export/ascor/assessment_indicators.rb @@ -0,0 +1,30 @@ +module CSVExport + module ASCOR + class AssessmentIndicators + HEADERS = ['Id', 'Type', 'Code', 'Text', 'Units or response type'].freeze + + def call + # BOM UTF-8 + CSV.generate("\xEF\xBB\xBF") do |csv| + csv << HEADERS + + assessment_indicators.each do |indicator| + csv << [ + indicator.id, + indicator.indicator_type, + indicator.code, + indicator.text, + indicator.units_or_response_type + ] + end + end + end + + private + + def assessment_indicators + @assessment_indicators ||= ::ASCOR::AssessmentIndicator.order(:id) + end + end + end +end diff --git a/app/services/csv_export/ascor/assessments.rb b/app/services/csv_export/ascor/assessments.rb new file mode 100644 index 000000000..460ddabd6 --- /dev/null +++ b/app/services/csv_export/ascor/assessments.rb @@ -0,0 +1,72 @@ +module CSVExport + module ASCOR + class Assessments + def call + CSV.generate("\xEF\xBB\xBF") do |csv| + csv << headers + + assessments.each do |assessment| + csv << [ + assessment.id, + assessment.assessment_date, + assessment.publication_date, + assessment.country_id, + assessment.country.name, + *answer_values_for(assessment), + *source_values_for(assessment), + *year_values_for(assessment), + assessment.notes + ] + end + end + end + + private + + def headers + result = ['Id', 'Assessment date', 'Publication date', 'Country Id', 'Country'] + result += assessment_indicators.reject { |i| i.indicator_type == 'pillar' || i.code.in?(%w[EP.1.a.i EP.1.a.ii]) } + .map { |i| "#{i.indicator_type} #{i.code}" } + result += assessment_indicators.select { |i| i.indicator_type.in?(%w[indicator metric]) } + .map { |i| "source #{i.indicator_type} #{i.code}" } + result += assessment_indicators.select { |i| i.indicator_type == 'metric' } + .map { |i| "year #{i.indicator_type} #{i.code}" } + result += ['Notes'] + result + end + + def answer_values_for(assessment) + assessment_indicators.reject { |i| i.indicator_type == 'pillar' || i.code.in?(%w[EP.1.a.i EP.1.a.ii]) } + .map do |indicator| + assessment_results[[assessment.id, indicator.id]]&.first&.answer + end + end + + def source_values_for(assessment) + assessment_indicators.select { |i| i.indicator_type.in?(%w[indicator metric]) } + .map do |indicator| + assessment_results[[assessment.id, indicator.id]]&.first&.source + end + end + + def year_values_for(assessment) + assessment_indicators.select { |i| i.indicator_type == 'metric' } + .map do |indicator| + assessment_results[[assessment.id, indicator.id]]&.first&.year + end + end + + def assessments + @assessments ||= ::ASCOR::Assessment.joins(:country).includes(:country).order(:assessment_date, 'ascor_countries.name') + end + + def assessment_results + @assessment_results ||= ::ASCOR::AssessmentResult.all.group_by { |r| [r.assessment_id, r.indicator_id] } + end + + def assessment_indicators + @assessment_indicators ||= ::ASCOR::AssessmentIndicator.order(:id) + end + end + end +end diff --git a/app/services/csv_export/ascor/benchmarks.rb b/app/services/csv_export/ascor/benchmarks.rb new file mode 100644 index 000000000..2ce163a71 --- /dev/null +++ b/app/services/csv_export/ascor/benchmarks.rb @@ -0,0 +1,46 @@ +module CSVExport + module ASCOR + class Benchmarks + HEADERS = [ + 'Id', + 'Country', + 'Publication date', + 'Emissions metric', + 'Emissions boundary', + 'Units', + 'Benchmark type' + ].freeze + + def call + CSV.generate("\xEF\xBB\xBF") do |csv| + csv << (HEADERS + year_columns) + + benchmarks.each do |benchmark| + csv << [ + benchmark.id, + benchmark.country.name, + benchmark.publication_date, + benchmark.emissions_metric, + benchmark.emissions_boundary, + benchmark.units, + benchmark.benchmark_type, + year_columns.map do |year| + benchmark.emissions[year] + end + ].flatten + end + end + end + + private + + def year_columns + @year_columns ||= benchmarks.flat_map(&:emissions_all_years).uniq.sort + end + + def benchmarks + @benchmarks ||= ::ASCOR::Benchmark.joins(:country).includes(:country).order('ascor_countries.name') + end + end + end +end diff --git a/app/services/csv_export/ascor/countries.rb b/app/services/csv_export/ascor/countries.rb new file mode 100644 index 000000000..8c5b6ae61 --- /dev/null +++ b/app/services/csv_export/ascor/countries.rb @@ -0,0 +1,39 @@ +module CSVExport + module ASCOR + class Countries + HEADERS = [ + 'Id', + 'Name', + 'Country ISO code', + 'Region', + 'World Bank lending group', + 'International Monetary Fund fiscal monitor category', + 'Type of Party to the United Nations Framework Convention on Climate Change' + ].freeze + + def call + CSV.generate("\xEF\xBB\xBF") do |csv| + csv << HEADERS + + countries.each do |country| + csv << [ + country.id, + country.name, + country.iso, + country.region, + country.wb_lending_group, + country.fiscal_monitor_category, + country.type_of_party + ] + end + end + end + + private + + def countries + @countries ||= ::ASCOR::Country.order(:name) + end + end + end +end diff --git a/app/services/csv_export/ascor/pathways.rb b/app/services/csv_export/ascor/pathways.rb new file mode 100644 index 000000000..d54a4e253 --- /dev/null +++ b/app/services/csv_export/ascor/pathways.rb @@ -0,0 +1,65 @@ +module CSVExport + module ASCOR + class Pathways + HEADERS = [ + 'Id', + 'Country', + 'Emissions metric', + 'Emissions boundary', + 'Units', + 'Assessment date', + 'Publication date', + 'Last historical year', + 'metric EP1.a.i', + 'source metric EP1.a.i', + 'year metric EP1.a.i', + 'metric EP1.a.ii 1-year', + 'metric EP1.a.ii 3-year', + 'metric EP1.a.ii 5-year', + 'source metric EP1.a.ii', + 'year metric EP1.a.ii' + ].freeze + + def call + CSV.generate("\xEF\xBB\xBF") do |csv| + csv << (HEADERS + year_columns) + + pathways.each do |pathway| + csv << [ + pathway.id, + pathway.country.name, + pathway.emissions_metric, + pathway.emissions_boundary, + pathway.units, + pathway.assessment_date, + pathway.publication_date, + pathway.last_historical_year, + pathway.recent_emission_level, + pathway.recent_emission_source, + pathway.recent_emission_year, + pathway.trend_1_year, + pathway.trend_3_year, + pathway.trend_5_year, + pathway.trend_source, + pathway.trend_year, + year_columns.map do |year| + pathway.emissions[year] + end + ].flatten + end + end + end + + private + + def year_columns + @year_columns ||= pathways.flat_map(&:emissions_all_years).uniq.sort + end + + def pathways + @pathways ||= ::ASCOR::Pathway.joins(:country).includes(:country) + .order(:assessment_date, 'ascor_countries.name') + end + end + end +end diff --git a/app/services/csv_import/ascor_assessment_indicators.rb b/app/services/csv_import/ascor_assessment_indicators.rb new file mode 100644 index 000000000..b45ad20d6 --- /dev/null +++ b/app/services/csv_import/ascor_assessment_indicators.rb @@ -0,0 +1,41 @@ +module CSVImport + class ASCORAssessmentIndicators < BaseImporter + include Helpers + + def import + import_each_csv_row(csv) do |row| + indicator = prepare_indicator(row) + + indicator.indicator_type = row[:type].downcase + indicator.code = row[:code] + indicator.text = row[:text] + indicator.units_or_response_type = row[:units_or_response_type] if row.header?(:units_or_response_type) + + was_new_record = indicator.new_record? + any_changes = indicator.changed? + + indicator.save! + + update_import_results(was_new_record, any_changes) + end + end + + private + + def resource_klass + ASCOR::AssessmentIndicator + end + + def required_headers + [:id, :code, :type, :text] + end + + def prepare_indicator(row) + find_record_by(:id, row) || + resource_klass.find_or_initialize_by( + code: row[:code], + indicator_type: row[:type] + ) + end + end +end diff --git a/app/services/csv_import/ascor_assessments.rb b/app/services/csv_import/ascor_assessments.rb new file mode 100644 index 000000000..c9c6e4740 --- /dev/null +++ b/app/services/csv_import/ascor_assessments.rb @@ -0,0 +1,70 @@ +module CSVImport + class ASCORAssessments < BaseImporter + include Helpers + + def import + import_each_csv_row(csv) do |row| + assessment = prepare_assessment(row) + + assessment.country = countries[row[:country]].first if row.header?(:country) + assessment.assessment_date = assessment_date(row) if row.header?(:assessment_date) + assessment.publication_date = publication_date(row) if row.header?(:publication_date) + assessment.notes = row[:notes] if row.header?(:notes) + + was_new_record = assessment.new_record? + + assessment.save! + save_assessment_results! assessment, row + + update_import_results(was_new_record, !was_new_record) + end + end + + private + + def header_converters + converter = lambda { |header| header.squish.tr(' ', '_').downcase.underscore.to_sym } + [converter] + end + + def resource_klass + ASCOR::Assessment + end + + def required_headers + [:id] + end + + def prepare_assessment(row) + find_record_by(:id, row) || + ASCOR::Assessment.find_or_initialize_by( + country: countries[row[:country]].first, + assessment_date: assessment_date(row) + ) + end + + def save_assessment_results!(assessment, row) + ASCOR::AssessmentIndicator.all.each do |indicator| + value_key = "#{indicator.indicator_type}_#{indicator.code.underscore}".to_sym + result = ASCOR::AssessmentResult.find_or_initialize_by(assessment: assessment, indicator: indicator) + result.answer = row[value_key] if row.header?(value_key) + [:source, :year].each do |attr| + result.public_send("#{attr}=", row["#{attr}_#{value_key}".to_sym]) if row.header?("#{attr}_#{value_key}".to_sym) + end + result.save! + end + end + + def countries + @countries ||= ASCOR::Country.all.group_by(&:name) + end + + def assessment_date(row) + CSVImport::DateUtils.safe_parse!(row[:assessment_date], ['%Y-%m-%d', '%m/%d/%y']) if row[:assessment_date] + end + + def publication_date(row) + CSVImport::DateUtils.safe_parse!(row[:publication_date], ['%Y-%m']) + end + end +end diff --git a/app/services/csv_import/ascor_benchmarks.rb b/app/services/csv_import/ascor_benchmarks.rb new file mode 100644 index 000000000..2be256985 --- /dev/null +++ b/app/services/csv_import/ascor_benchmarks.rb @@ -0,0 +1,54 @@ +module CSVImport + class ASCORBenchmarks < BaseImporter + include Helpers + + def import + import_each_csv_row(csv) do |row| + benchmark = prepare_benchmark(row) + + benchmark.country = countries[row[:country]].first if row.header?(:country) + benchmark.publication_date = parse_date(row[:publication_date]) if row.header?(:publication_date) + benchmark.emissions_metric = row[:emissions_metric] if row.header?(:emissions_metric) + benchmark.emissions_boundary = row[:emissions_boundary] if row.header?(:emissions_boundary) + benchmark.units = row[:units] if row.header?(:units) + benchmark.benchmark_type = row[:benchmark_type] if row.header?(:benchmark_type) + benchmark.emissions = parse_emissions(row, thousands_separator: ',') if emission_headers?(row) + + was_new_record = benchmark.new_record? + any_changes = benchmark.changed? + + benchmark.save! + + update_import_results(was_new_record, any_changes) + end + end + + private + + def resource_klass + ASCOR::Benchmark + end + + def required_headers + [:id] + end + + def prepare_benchmark(row) + find_record_by(:id, row) || + ASCOR::Benchmark.find_or_initialize_by( + country: countries[row[:country]].first, + emissions_metric: row[:emissions_metric], + emissions_boundary: row[:emissions_boundary], + benchmark_type: row[:benchmark_type] + ) + end + + def countries + @countries ||= ASCOR::Country.all.group_by(&:name) + end + + def parse_date(date) + CSVImport::DateUtils.safe_parse!(date, ['%Y-%m', '%Y-%m-%d']) + end + end +end diff --git a/app/services/csv_import/ascor_countries.rb b/app/services/csv_import/ascor_countries.rb new file mode 100644 index 000000000..24cf8f6fd --- /dev/null +++ b/app/services/csv_import/ascor_countries.rb @@ -0,0 +1,45 @@ +module CSVImport + class ASCORCountries < BaseImporter + include Helpers + + def import + import_each_csv_row(csv) do |row| + country = prepare_country row + + country.name = row[:name] if row.header?(:name) + country.iso = row[:country_iso_code] if row.header?(:country_iso_code) + country.region = row[:region] if row.header?(:region) + country.wb_lending_group = row[:world_bank_lending_group] if row.header?(:world_bank_lending_group) + if row.header?(:international_monetary_fund_fiscal_monitor_category) + country.fiscal_monitor_category = row[:international_monetary_fund_fiscal_monitor_category] + end + if row.header?(:type_of_party_to_the_united_nations_framework_convention_on_climate_change) + country.type_of_party = row[:type_of_party_to_the_united_nations_framework_convention_on_climate_change] + end + + was_new_record = country.new_record? + any_changes = country.changed? + + country.save! + + update_import_results(was_new_record, any_changes) + end + end + + private + + def resource_klass + ASCOR::Country + end + + def required_headers + [:id] + end + + def prepare_country(row) + find_record_by(:id, row) || + find_record_by(:iso, row, column_name: :country_iso_code) || + resource_klass.new + end + end +end diff --git a/app/services/csv_import/ascor_pathways.rb b/app/services/csv_import/ascor_pathways.rb new file mode 100644 index 000000000..3322d7b97 --- /dev/null +++ b/app/services/csv_import/ascor_pathways.rb @@ -0,0 +1,69 @@ +module CSVImport + class ASCORPathways < BaseImporter + include Helpers + + def import + import_each_csv_row(csv) do |row| + pathway = prepare_pathway(row) + + pathway.country = countries[row[:country]].first if row.header?(:country) + pathway.emissions_metric = row[:emissions_metric] if row.header?(:emissions_metric) + pathway.emissions_boundary = row[:emissions_boundary] if row.header?(:emissions_boundary) + pathway.units = row[:units] if row.header?(:units) + pathway.assessment_date = assessment_date(row) if row.header?(:assessment_date) + pathway.publication_date = publication_date(row) if row.header?(:publication_date) + pathway.last_historical_year = row[:last_historical_year] if row.header?(:last_historical_year) + pathway.trend_1_year = row[:metric_ep1aii_1year] if row.header?(:metric_ep1aii_1year) + pathway.trend_3_year = row[:metric_ep1aii_3year] if row.header?(:metric_ep1aii_3year) + pathway.trend_5_year = row[:metric_ep1aii_5year] if row.header?(:metric_ep1aii_5year) + pathway.trend_source = row[:source_metric_ep1aii] if row.header?(:source_metric_ep1aii) + pathway.trend_year = row[:year_metric_ep1aii] if row.header?(:year_metric_ep1aii) + if row.header?(:metric_ep1ai) + pathway.recent_emission_level = string_to_float(row[:metric_ep1ai], thousands_separator: ',') + end + pathway.recent_emission_source = row[:source_metric_ep1ai] if row.header?(:source_metric_ep1ai) + pathway.recent_emission_year = row[:year_metric_ep1ai] if row.header?(:year_metric_ep1ai) + pathway.emissions = parse_emissions(row, thousands_separator: ',') if emission_headers?(row) + + was_new_record = pathway.new_record? + any_changes = pathway.changed? + + pathway.save! + + update_import_results(was_new_record, any_changes) + end + end + + private + + def resource_klass + ASCOR::Pathway + end + + def required_headers + [:id] + end + + def prepare_pathway(row) + find_record_by(:id, row) || + ASCOR::Pathway.find_or_initialize_by( + country: countries[row[:country]].first, + emissions_metric: row[:emissions_metric], + emissions_boundary: row[:emissions_boundary], + assessment_date: assessment_date(row) + ) + end + + def countries + @countries ||= ASCOR::Country.all.group_by(&:name) + end + + def assessment_date(row) + CSVImport::DateUtils.safe_parse!(row[:assessment_date], ['%Y-%m-%d', '%m/%d/%y']) if row[:assessment_date] + end + + def publication_date(row) + CSVImport::DateUtils.safe_parse!(row[:publication_date], ['%Y-%m']) + end + end +end diff --git a/app/services/csv_import/base_importer.rb b/app/services/csv_import/base_importer.rb index e0812a66c..3d5903a26 100644 --- a/app/services/csv_import/base_importer.rb +++ b/app/services/csv_import/base_importer.rb @@ -51,8 +51,8 @@ def csv @csv ||= parse_csv end - def find_record_by(attr_name, row) - resource_klass.find_by(attr_name.to_sym => row[attr_name]&.strip) + def find_record_by(attr_name, row, column_name: nil) + resource_klass.find_by(attr_name.to_sym => row[column_name || attr_name]&.strip) end def prepare_overridden_resource(row) diff --git a/app/services/csv_import/helpers/emissions.rb b/app/services/csv_import/helpers/emissions.rb index 8e93ea2ad..2abae9907 100644 --- a/app/services/csv_import/helpers/emissions.rb +++ b/app/services/csv_import/helpers/emissions.rb @@ -3,17 +3,24 @@ module Helpers module Emissions EMISSION_YEAR_PATTERN = /^\d{4}$/.freeze - def parse_emissions(row) + def parse_emissions(row, thousands_separator: '') row.headers.grep(EMISSION_YEAR_PATTERN).reduce({}) do |acc, year| - next acc unless row[year].present? + next acc if row[year].blank? || row[year] == 'NA' - acc.merge(year.to_s.to_i => row[year].to_f) + acc.merge(year.to_s.to_i => string_to_float(row[year], thousands_separator: thousands_separator)) end end def emission_headers?(row) row.headers.grep(EMISSION_YEAR_PATTERN).any? end + + def string_to_float(string, thousands_separator: ',') + return nil if string.blank? + return string.to_f unless string.is_a?(String) + + string.delete(thousands_separator).delete("\t").delete(' ').to_f + end end end end diff --git a/app/services/seed/tpi_data.rb b/app/services/seed/tpi_data.rb index 2eec3df20..ba67a6ea5 100644 --- a/app/services/seed/tpi_data.rb +++ b/app/services/seed/tpi_data.rb @@ -59,6 +59,26 @@ def call TimedLogger.log('Create Publications') do create_publications end + + TimedLogger.log('Import ASCOR Countries') do + run_importer CSVImport::ASCORCountries.new(seed_file('ascor_countries.csv')) + end + + TimedLogger.log('Import ASCOR Benchmarks') do + run_importer CSVImport::ASCORBenchmarks.new(seed_file('ascor_benchmarks.csv')) + end + + TimedLogger.log('Import ASCOR Pathways') do + run_importer CSVImport::ASCORPathways.new(seed_file('ascor_pathways.csv')) + end + + TimedLogger.log('Import ASCOR Assessment Indicators') do + run_importer CSVImport::ASCORAssessmentIndicators.new(seed_file('ascor_assessment_indicators.csv')) + end + + TimedLogger.log('Import ASCOR Assessments') do + run_importer CSVImport::ASCORAssessments.new(seed_file('ascor_assessments.csv')) + end end def import_sector_clusters @@ -190,6 +210,7 @@ def import_sectors end end TPISector.find_or_create_by!(name: 'Banks', show_in_tpi_tool: false) + TPISector.find_or_create_by!(name: 'ASCOR', show_in_tpi_tool: false) end end end diff --git a/app/views/layouts/tpi/_banner.html.erb b/app/views/layouts/tpi/_banner.html.erb index 6d60362ab..53b57fe2d 100644 --- a/app/views/layouts/tpi/_banner.html.erb +++ b/app/views/layouts/tpi/_banner.html.erb @@ -10,26 +10,7 @@ /> <% end %>
    -
    -

    Hosted by:

    - - -
    + <%= content_for?(:hosted_by) ? content_for(:hosted_by) : render('layouts/tpi/hosted_by') %>
    diff --git a/app/views/layouts/tpi/_header.html.erb b/app/views/layouts/tpi/_header.html.erb index 2ba23f620..d912fbc09 100644 --- a/app/views/layouts/tpi/_header.html.erb +++ b/app/views/layouts/tpi/_header.html.erb @@ -16,8 +16,8 @@ path: tpi_banks_path }, { - title: 'Sovereign bonds issuers', - path: tpi_publications_path(tags: 'ASCOR') + title: 'ASCOR', + path: tpi_ascor_index_path } ], active: active_menu_page?([ diff --git a/app/views/layouts/tpi/_hosted_by.html.erb b/app/views/layouts/tpi/_hosted_by.html.erb new file mode 100644 index 000000000..404c1b97b --- /dev/null +++ b/app/views/layouts/tpi/_hosted_by.html.erb @@ -0,0 +1,20 @@ +
    +

    Hosted by:

    + + +
    \ No newline at end of file diff --git a/app/views/tpi/ascor/_assessment.html.erb b/app/views/tpi/ascor/_assessment.html.erb new file mode 100644 index 000000000..1db2dd630 --- /dev/null +++ b/app/views/tpi/ascor/_assessment.html.erb @@ -0,0 +1,69 @@ +<% pillars = ASCOR::AssessmentIndicator.pillar.order(:id) %> +<% areas = ASCOR::AssessmentIndicator.area.order(:id) %> +<% indicators = ASCOR::AssessmentIndicator.indicator.order(:id) %> +<% metrics = ASCOR::AssessmentIndicator.metric.order(:id) %> + +<%= react_component('AscorQuestionLegend') %> +<% pillars.each_with_index do |pillar, i| %> +
    +
    <%= "Pillar #{i + 1}" %>
    +

    <%= pillar.text %>

    + + <% ascor_sub_indicators_for(pillar, areas).each do |area| %> + +
    +
    +
    +
    <%= "Area #{area.code}" %>
    +
    <%= area.text %>
    +
    + +
    + +
    + <% ascor_sub_indicators_for(area, indicators).each do |indicator| %> +
    +
    +
    + <%= "#{indicator.code.split('.').last}. #{indicator.text}" %> +
    + <% if ascor_assessment_result_for(indicator, @assessment).source.present? %> +
    + <%= link_to 'Source', ascor_assessment_result_for(indicator, @assessment).source %> +
    + <% end %> +
    + + <% ascor_sub_indicators_for(indicator, metrics).each do |metric| %> + <% next if metric.code == 'EP.1.a.ii' # skipped because EP.1.a.i and EP.1.a.ii are rendered via same React component %> + +
    + <% if metric.code == 'EP.1.a.i' %> + <%= render 'tpi/ascor/metrics_ep1a', recent_emissions: @recent_emissions %> + <% else %> +
    +
    + <%= "#{metric.code.split('.').last}. #{metric.text}" %> +
    + <% if ascor_assessment_result_for(metric, @assessment).source.present? %> +
    + <%= link_to "Source (#{ascor_assessment_result_for(metric, @assessment).year})", ascor_assessment_result_for(metric, @assessment).source %> +
    + <% end %> + <% if ascor_assessment_result_for(metric, @assessment).answer.present? %> +
    +
    + <%= ascor_assessment_result_for(metric, @assessment).answer %> +
    + <% end %> +
    + <% end %> +
    + <% end %> +
    + <% end %> +
    +
    + <% end %> +
    +<% end %> \ No newline at end of file diff --git a/app/views/tpi/ascor/_bubble_chart.html.erb b/app/views/tpi/ascor/_bubble_chart.html.erb new file mode 100644 index 000000000..93395cd03 --- /dev/null +++ b/app/views/tpi/ascor/_bubble_chart.html.erb @@ -0,0 +1,7 @@ +<%= react_component('charts/ascor-bubble/Chart', { results: @ascor_assessment_results }) %> +<%= react_component('InfoModal', { + title: "ASCOR country assessment results", + text: "

    Countries are assessed across the ASCOR framework’s three pillars and thirteen topic areas.

    " \ + "

    The area-level result is Yes if all indicators within the area are assessed as Yes, Partial if some of the indicators within the area are assessed as Yes, and No if all of the indicators within the area are assessed as No.

    ", + element: "#bubble-chart-info" +}) %> \ No newline at end of file diff --git a/app/views/tpi/ascor/_contact.html.erb b/app/views/tpi/ascor/_contact.html.erb new file mode 100644 index 000000000..a06418e1f --- /dev/null +++ b/app/views/tpi/ascor/_contact.html.erb @@ -0,0 +1,8 @@ +
    +
    +
    +

    Get in touch with us for enquiries.

    +

    At ASCOR, we are here to help and answer any questions you may have. Contact us at gri.ascor@lse.ac.uk.

    + Contact ASCOR +
    +
    \ No newline at end of file diff --git a/app/views/tpi/ascor/_emissions_chart.html.erb b/app/views/tpi/ascor/_emissions_chart.html.erb new file mode 100644 index 000000000..e4e69777a --- /dev/null +++ b/app/views/tpi/ascor/_emissions_chart.html.erb @@ -0,0 +1,16 @@ +<%= react_component('charts/ascor-emissions', { + emissions_metric_filter: ASCOR::EmissionsMetric::VALUES, + default_emissions_metric_filter: 'Absolute', + emissions_boundary_filter: ASCOR::EmissionsBoundary::VALUES, + default_emissions_boundary_filter: 'Production - excluding LULUCF', + countries: ASCOR::Country.all.sort_by(&:name).map { |c| { id: c.id, iso: c.iso, name: c.name } }, + default_countries: ASCOR::Country.where(iso: ASCOR::Country::DEFAULT_COUNTRIES).map(&:id), + emissions_data_url: emissions_chart_data_tpi_ascor_index_path +}) %> +<%= react_component('InfoModal', { + title: "Country emission pathways", + text: "

    Country emission pathways are assessed in several ways to account for a variety of factors and uncertainties. The emission metrics considered include the following options: production and consumption-based emissions; exclusion of LULUCF emissions and LULUCF emissions alone; and emissions on an absolute and intensity basis (per capita and per PPP-adjusted GDP).

    " \ + "

    Targeted future pathways are included only for absolute production-based emissions excluding LULUCF as this is the basis on which 2030 targets are assessed.

    " \ + "

    Pathways end in 2030 because long-term net zero targets are often stated on a different emission boundary from the one considered in the 2030 target assessments (e.g. including only CO₂ emissions rather than all Kyoto greenhouse gases).

    ", + element: "#emissions-chart-info" +}) %> \ No newline at end of file diff --git a/app/views/tpi/ascor/_index_assessment.js.erb b/app/views/tpi/ascor/_index_assessment.js.erb new file mode 100644 index 000000000..9196d3334 --- /dev/null +++ b/app/views/tpi/ascor/_index_assessment.js.erb @@ -0,0 +1,7 @@ +(function () { + document.getElementById('bubble-chart').innerHTML = "<%= j render('bubble_chart') %>"; + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('assessment_date', <%= @assessment_date %>); + window.history.replaceState({}, '', newUrl.href); + ReactRailsUJS.mountComponents('#bubble-chart'); +})(); diff --git a/app/views/tpi/ascor/_index_emissions_assessment.js.erb b/app/views/tpi/ascor/_index_emissions_assessment.js.erb new file mode 100644 index 000000000..8a922fca4 --- /dev/null +++ b/app/views/tpi/ascor/_index_emissions_assessment.js.erb @@ -0,0 +1,7 @@ +(function () { + document.getElementById('emissions-chart').innerHTML = "<%= j render('emissions_chart') %>"; + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('emissions_assessment_date', <%= @assessment_date %>); + window.history.replaceState({}, '', newUrl.href); + ReactRailsUJS.mountComponents('#emissions-charts'); +})(); diff --git a/app/views/tpi/ascor/_metrics_ep1a.html.erb b/app/views/tpi/ascor/_metrics_ep1a.html.erb new file mode 100644 index 000000000..e240fc3b6 --- /dev/null +++ b/app/views/tpi/ascor/_metrics_ep1a.html.erb @@ -0,0 +1,9 @@ +<%= react_component('AscorRecentEmissions', { + emissions_metric_filter: ASCOR::EmissionsMetric::VALUES, + default_emissions_metric_filter: 'Absolute', + emissions_boundary_filter: ASCOR::EmissionsBoundary::VALUES, + default_emissions_boundary_filter: 'Production - excluding LULUCF', + trend_filters: ['1 year trend', '3 years trend', '5 years trend'], + default_trend_filter: '1 year trend', + data: recent_emissions +}) %> \ No newline at end of file diff --git a/app/views/tpi/ascor/_show_assessment.js.erb b/app/views/tpi/ascor/_show_assessment.js.erb new file mode 100644 index 000000000..1cccca213 --- /dev/null +++ b/app/views/tpi/ascor/_show_assessment.js.erb @@ -0,0 +1,7 @@ +(function () { + document.getElementById('assessment').innerHTML = "<%= j render('assessment') %>"; + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('assessment_date', <%= @assessment_date %>); + window.history.replaceState({}, '', newUrl.href); + ReactRailsUJS.mountComponents('#assessment'); +})(); \ No newline at end of file diff --git a/app/views/tpi/ascor/index.html.erb b/app/views/tpi/ascor/index.html.erb new file mode 100644 index 000000000..a6879d5af --- /dev/null +++ b/app/views/tpi/ascor/index.html.erb @@ -0,0 +1,134 @@ +<% content_for :page_title, "ASCOR Tool - Transition Pathway Initiative" %> +<% content_for :hosted_by do %><% end %> + +
    + + +
    +
    +

    + ASCOR country assessment results + ? +

    + +
    +
    + Assessment Date: +
    +
    + <%= react_component('RemoteDropdown', { + name: 'assessment_date', + remote: true, + url: index_assessment_tpi_ascor_index_path, + data: @assessment_dates.map {|v| {label: v&.strftime('%d %B %Y'), value: v}}, + selected: @assessment_date + }) %> +
    +
    +
    +
    + Countries are assessed across the ASCOR framework’s three pillars and thirteen topic areas. +
    +
    + <%= render 'tpi/ascor/bubble_chart' %> +
    +
    + +
    +
    +

    + Country emission pathways + ? +

    + +
    +
    + Assessment Date: +
    +
    + <%= react_component('RemoteDropdown', { + name: 'emissions_assessment_date', + remote: true, + url: index_emissions_assessment_tpi_ascor_index_path, + data: @assessment_dates.map {|v| {label: v&.strftime('%d %B %Y'), value: v}}, + selected: @emissions_assessment_date + }) %> +
    +
    +
    +
    + Country emissions assessed using different metrics, focusing on absolute production-based emissions, excluding LULUCF, to align with 2030 targets. +
    +
    + <%= render 'tpi/ascor/emissions_chart' %> +
    +
    + + <% if @methodology_publication.present? %> +
    +
    +
    +

    Methodology

    + +
    + <%= @methodology_description&.text&.html_safe %> +
    +
    +
    + <%= render 'tpi/publications/list', publications_and_articles: [@methodology_publication] %> +
    +
    +
    + <% end %> + +
    + + + + <%= render 'contact' %> +
    \ No newline at end of file diff --git a/app/views/tpi/ascor/show.html.erb b/app/views/tpi/ascor/show.html.erb new file mode 100644 index 000000000..c38d0940f --- /dev/null +++ b/app/views/tpi/ascor/show.html.erb @@ -0,0 +1,43 @@ +<% content_for :page_title, "#{@country.name} - Transition Pathway Initiative" %> +<% content_for :hosted_by do %><% end %> + +
    + + + <% if @assessment.present? %> +
    + <%= render 'assessment', assessment: @assessment %> +
    + <% end %> + + <%= render 'contact' %> +
    \ No newline at end of file diff --git a/app/views/tpi/companies/show.html.erb b/app/views/tpi/companies/show.html.erb index 3ad25bccf..90cf136bc 100644 --- a/app/views/tpi/companies/show.html.erb +++ b/app/views/tpi/companies/show.html.erb @@ -15,7 +15,7 @@
    - <%= render 'tpi/shared/mq_beta_scores' %> + <%= render 'tpi/shared/mq_beta_scores', has_data: @company.beta_mq_assessments? %>
    - <%= render 'tpi/shared/mq_beta_scores' %> + <%= render 'tpi/shared/mq_beta_scores', has_data: @company.beta_mq_assessments? %>
    Management Quality diff --git a/app/views/tpi/home/index.html.erb b/app/views/tpi/home/index.html.erb index 42484d8ac..980f009c9 100644 --- a/app/views/tpi/home/index.html.erb +++ b/app/views/tpi/home/index.html.erb @@ -37,7 +37,7 @@
    Climate assessments:
    Banks
    <% end %> - <%= link_to tpi_publications_path(tags: 'ASCOR'), class: 'assessment-box sovereign-bonds' do %> + <%= link_to tpi_publications_path(tags: 'ASCOR'), class: 'assessment-box ascor' do %>
    Climate assessments:
    Sovereign bond issuers
    <% end %> diff --git a/app/views/tpi/sectors/index.html.erb b/app/views/tpi/sectors/index.html.erb index 727797c58..ebf106a3f 100644 --- a/app/views/tpi/sectors/index.html.erb +++ b/app/views/tpi/sectors/index.html.erb @@ -18,7 +18,7 @@
    - <%= render 'tpi/shared/mq_beta_scores' %> + <%= render 'tpi/shared/mq_beta_scores', has_data: true %>
    arrow down diff --git a/app/views/tpi/sectors/show.html.erb b/app/views/tpi/sectors/show.html.erb index e668fa450..ca740b175 100644 --- a/app/views/tpi/sectors/show.html.erb +++ b/app/views/tpi/sectors/show.html.erb @@ -18,7 +18,7 @@
    - <%= render 'tpi/shared/mq_beta_scores' %> + <%= render 'tpi/shared/mq_beta_scores', has_data: true %>
    - <%= render 'tpi/shared/mq_beta_scores' %> + <%= render 'tpi/shared/mq_beta_scores', has_data: true %>
    Management Quality diff --git a/app/views/tpi/shared/_mq_beta_scores.html.erb b/app/views/tpi/shared/_mq_beta_scores.html.erb index 9245bd2ba..37acc0d0a 100644 --- a/app/views/tpi/shared/_mq_beta_scores.html.erb +++ b/app/views/tpi/shared/_mq_beta_scores.html.erb @@ -1,4 +1,8 @@ <% if ENV['MQ_BETA_ENABLED'].to_s == 'true' %> + <% link_html = link_to(enable_beta_data_tpi_mq_assessments_path, + class: class_names('button', 'is-primary', 'mq-beta-scores__beta-button', 'active': session[:enable_beta_mq_assessments], 'disabled': !has_data), + style: "min-width: 0;") { "BETA (V5.0)" } %> +
    MQ Methodology <%= link_to disable_beta_data_tpi_mq_assessments_path, @@ -6,11 +10,15 @@ style: "min-width: 0;" do %> Current (V4.0) <% end %> - <%= link_to enable_beta_data_tpi_mq_assessments_path, - class: class_names('button', 'is-primary', 'mq-beta-scores__beta-button', 'active': session[:enable_beta_mq_assessments]), - style: "min-width: 0;" do %> - BETA (V5.0) - <% end %> + <% if has_data %> + <%= link_html %> + <% else %> + <%= react_component("InfoTooltip", { + trigger: link_html, + content: 'It is not currently possible to show Beta (V5.0) scores for this company. Scores for the current methodology (V4.0) are shown in their place.', + html: true, + }) %> + <% end %>
    <% end %> \ No newline at end of file diff --git a/config/brakeman.ignore b/config/brakeman.ignore index 4238fb329..95bf21a29 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,5 +1,36 @@ { "ignored_warnings": [ + { + "warning_type": "Cross-Site Scripting", + "warning_code": 2, + "fingerprint": "16cf341536f52272758f6cfb2dd18fc4c78ff00efbc1055a560e986e2f73af0c", + "check_name": "CrossSiteScripting", + "message": "Unescaped model attribute", + "file": "app/views/tpi/ascor/index.html.erb", + "line": 36, + "link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting", + "code": "Content.find_by(:page => TPIPage.find_by(:slug => \"ascor\"), :code => \"methodology_description\").text", + "render_path": [ + { + "type": "controller", + "class": "TPI::ASCORController", + "method": "index", + "line": 15, + "file": "app/controllers/tpi/ascor_controller.rb", + "rendered": { + "name": "tpi/ascor/index", + "file": "app/views/tpi/ascor/index.html.erb" + } + } + ], + "location": { + "type": "template", + "template": "tpi/ascor/index" + }, + "user_input": null, + "confidence": "Medium", + "note": "" + }, { "warning_type": "Dynamic Render Path", "warning_code": 15, diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb index 44a21ae4b..753677f97 100644 --- a/config/initializers/active_admin.rb +++ b/config/initializers/active_admin.rb @@ -47,6 +47,7 @@ menu.add label: 'Geographies', priority: 1 menu.add label: 'Laws', priority: 2 menu.add label: 'TPI', priority: 3 + menu.add label: 'ASCOR', priority: 4 menu.add label: 'Tags', priority: 8 menu.add label: 'Administration', priority: 9 end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index af54e0533..05288b433 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -18,4 +18,5 @@ inflect.acronym 'TPI' inflect.acronym 'CSV' inflect.acronym 'HTML' + inflect.acronym 'ASCOR' end diff --git a/config/routes.rb b/config/routes.rb index 0eec8873d..856d9837b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -54,6 +54,18 @@ end end + resources :ascor, only: [:show, :index] do + member do + get :show_assessment + end + collection do + get :index_assessment + get :index_emissions_assessment + get :emissions_chart_data + get :user_download + end + end + resources :publications, only: [:index, :show] do member do get :show_news_article diff --git a/db/migrate/20230912074824_create_ascor_countries.rb b/db/migrate/20230912074824_create_ascor_countries.rb new file mode 100644 index 000000000..5c960ab3a --- /dev/null +++ b/db/migrate/20230912074824_create_ascor_countries.rb @@ -0,0 +1,14 @@ +class CreateASCORCountries < ActiveRecord::Migration[6.1] + def change + create_table :ascor_countries do |t| + t.string :name, index: { unique: true } + t.string :slug, index: { unique: true } + t.string :iso, index: { unique: true } + t.string :region + t.string :wb_lending_group + t.string :fiscal_monitor_category + + t.timestamps + end + end +end diff --git a/db/migrate/20230912103652_create_ascor_benchmarks.rb b/db/migrate/20230912103652_create_ascor_benchmarks.rb new file mode 100644 index 000000000..56380cd3e --- /dev/null +++ b/db/migrate/20230912103652_create_ascor_benchmarks.rb @@ -0,0 +1,16 @@ +class CreateASCORBenchmarks < ActiveRecord::Migration[6.1] + def change + create_table :ascor_benchmarks do |t| + t.references :country, null: false, foreign_key: { to_table: :ascor_countries } + t.date :publication_date, null: true + t.string :emissions_metric + t.string :emissions_boundary + t.string :land_use + t.string :units + t.string :benchmark_type + t.jsonb :emissions, default: {} + + t.timestamps + end + end +end diff --git a/db/migrate/20230914065509_create_ascor_pathways.rb b/db/migrate/20230914065509_create_ascor_pathways.rb new file mode 100644 index 000000000..50ac610b8 --- /dev/null +++ b/db/migrate/20230914065509_create_ascor_pathways.rb @@ -0,0 +1,20 @@ +class CreateASCORPathways < ActiveRecord::Migration[6.1] + def change + create_table :ascor_pathways do |t| + t.references :country, null: false, foreign_key: { to_table: :ascor_countries } + t.string :emissions_metric + t.string :emissions_boundary + t.string :land_use + t.string :units + t.date :assessment_date + t.date :publication_date, null: true + t.integer :last_reported_year, null: true + t.string :trend_1_year, null: true + t.string :trend_3_year, null: true + t.string :trend_5_year, null: true + t.jsonb :emissions, default: {} + + t.timestamps + end + end +end diff --git a/db/migrate/20230914100201_create_ascor_assessment_indicators.rb b/db/migrate/20230914100201_create_ascor_assessment_indicators.rb new file mode 100644 index 000000000..7f37cf7d9 --- /dev/null +++ b/db/migrate/20230914100201_create_ascor_assessment_indicators.rb @@ -0,0 +1,11 @@ +class CreateASCORAssessmentIndicators < ActiveRecord::Migration[6.1] + def change + create_table :ascor_assessment_indicators do |t| + t.string :indicator_type + t.string :code + t.text :text + + t.timestamps + end + end +end diff --git a/db/migrate/20230915063557_create_ascor_assessments.rb b/db/migrate/20230915063557_create_ascor_assessments.rb new file mode 100644 index 000000000..8fef7f315 --- /dev/null +++ b/db/migrate/20230915063557_create_ascor_assessments.rb @@ -0,0 +1,13 @@ +class CreateASCORAssessments < ActiveRecord::Migration[6.1] + def change + create_table :ascor_assessments do |t| + t.references :country, null: false, foreign_key: { to_table: :ascor_countries } + t.date :assessment_date + t.date :publication_date, null: true + t.text :research_notes, null: true + t.text :further_information, null: true + + t.timestamps + end + end +end diff --git a/db/migrate/20230915064402_create_ascor_assessment_results.rb b/db/migrate/20230915064402_create_ascor_assessment_results.rb new file mode 100644 index 000000000..26d3e4918 --- /dev/null +++ b/db/migrate/20230915064402_create_ascor_assessment_results.rb @@ -0,0 +1,16 @@ +class CreateASCORAssessmentResults < ActiveRecord::Migration[6.1] + def change + create_table :ascor_assessment_results do |t| + t.references :assessment, null: false, foreign_key: { to_table: :ascor_assessments } + t.references :indicator, null: false, foreign_key: { to_table: :ascor_assessment_indicators } + t.string :answer, null: true + t.string :source_name, null: true + t.string :source_date, null: true + t.string :source_link, null: true + + t.timestamps + end + + add_index :ascor_assessment_results, [:assessment_id, :indicator_id], unique: true, name: 'assessment_results_on_assessment_id_and_indicator_id' + end +end diff --git a/db/migrate/20230920083300_refactor_ascor_models.rb b/db/migrate/20230920083300_refactor_ascor_models.rb new file mode 100644 index 000000000..7e48142bc --- /dev/null +++ b/db/migrate/20230920083300_refactor_ascor_models.rb @@ -0,0 +1,27 @@ +class RefactorASCORModels < ActiveRecord::Migration[6.1] + def change + ### ASCOR Countries ### + add_column :ascor_countries, :type_of_party, :string, null: true + ### ASCOR Benchmarks ### + remove_column :ascor_benchmarks, :land_use + ### ASCOR Pathways ### + remove_column :ascor_pathways, :land_use + add_column :ascor_pathways, :trend_source, :string, null: true + add_column :ascor_pathways, :trend_year, :integer, null: true + add_column :ascor_pathways, :recent_emission_level, :float, null: true + add_column :ascor_pathways, :recent_emission_source, :string, null: true + add_column :ascor_pathways, :recent_emission_year, :integer, null: true + rename_column :ascor_pathways, :last_reported_year, :last_historical_year + ### ASCOR Assessment Indicators ### + add_column :ascor_assessment_indicators, :units_or_response_type, :string, null: true + ### ASCOR Assessments ### + remove_column :ascor_assessments, :research_notes + remove_column :ascor_assessments, :further_information + add_column :ascor_assessments, :notes, :text, null: true + remove_column :ascor_assessment_results, :source_name + remove_column :ascor_assessment_results, :source_date + remove_column :ascor_assessment_results, :source_link + add_column :ascor_assessment_results, :source, :string, null: true + add_column :ascor_assessment_results, :year, :integer, null: true + end +end diff --git a/db/seeds/tpi/ascor_assessment_indicators.csv b/db/seeds/tpi/ascor_assessment_indicators.csv new file mode 100644 index 000000000..aa6c5b97c --- /dev/null +++ b/db/seeds/tpi/ascor_assessment_indicators.csv @@ -0,0 +1,83 @@ +Id,Type,Code,Text,Units or response type +,pillar,EP,Emissions Pathways, +,area,EP.1,Emissions Trends,Yes/No/Partial +,indicator,EP.1.a,Have the country’s emissions decreased in recent years?,Yes/No +,metric,EP.1.a.i,What is the country's most recent emissions level?,MtCO2e +,metric,EP.1.a.ii,What is the country's most recent emissions trend?,% +,indicator,EP.1.b,Is the most recent 3-year trend aligned with meeting the country's 1.5°C benchmark?,Yes/No +,indicator,EP.1.c,Is the most recent 3-year trend aligned with meeting the country’s 1.5°C fair share?,Yes/No +,area,EP.2,2030 Targets,Yes/No/Partial +,indicator,EP.2.a,Has the country set a 2030 emission reduction target?,Yes/No +,metric,EP.2.a.i,What is the target reduction relative to 2019 emissions?,% +,indicator,EP.2.b,Does the country specify whether and how much carbon credits may contribute to its 2030 target?,Yes/No +,metric,EP.2.b.i,What percentage of the 2030 target will be met using carbon credits?,% +,indicator,EP.2.c,Is the country’s 2030 target aligned with its national 1.5°C benchmark?,Yes/No +,metric,EP.2.c.i,What is the degree of alignment with its national 1.5°C benchmark?,% +,indicator,EP.2.d,Is the country’s 2030 target aligned with its national 1.5°C fair share?,Yes/No +,metric,EP.2.d.i,What is the degree of alignment with its national 1.5°C fair share?,% +,area,EP.3,Net Zero Targets,Yes/No/Partial +,indicator,EP.3.a,Has the country set a net zero CO2 target?,Yes/No +,metric,EP.3.a.i,In what year is the net zero CO2 target set?,Year +,indicator,EP.3.b,Is the country’s net zero CO2 target aligned with a global 1.5°C scenario? ,Yes/No +,indicator,EP.3.c,Is the country’s net zero CO2 target aligned with an accelerated deadline for high-income countries? ,Yes/No +,pillar,CP,Climate Policies, +,area,CP.1,Climate Legislation,Yes/No/Partial +,indicator,CP.1.a,Does the country have a framework climate law or equivalent?,Yes/No +,indicator,CP.1.b,Does the country's framework climate law specify key accountability elements?,Yes/No +,area,CP.2,Carbon Pricing,Yes/No/Partial +,indicator,CP.2.a,Does the country have a carbon pricing system?,Yes/No +,indicator,CP.2.b,Does the country's carbon pricing system(s) cover at least 50% of national GHG emissions?,Yes/No +,metric,CP.2.b.i,What percentage of national GHG emissions is covered by an explicit carbon price?,% +,indicator,CP.2.c,Is the carbon price at least at the floor of a global carbon price corridor aligned with the Paris Agreement?,Yes/No +,metric,CP.2.c.i,What is the country's most recent explicit carbon price?,US$/tCO2 +,area,CP.3,Fossil Fuels,Yes/No/Partial +,indicator,CP.3.a,Has the country committed to a deadline to phase out fossil fuel subsidies?,Yes/No +,metric,CP.3.a.i,By what year has the country committed to phase out fossil fuel subsidies?,Year +,indicator,CP.3.b,Does the country publish an inventory of direct fossil fuel subsidies? ,Yes/No +,metric,CP.3.b.i,How much is spent annually on explicit fossil fuel subsidies as a percentage of GDP?,% +,metric,CP.3.b.ii,What is the country's most recent 3-year trend in fossil fuel subsidies?,% +,indicator,CP.3.c,Has the country committed not to approve new coal mines?,Yes/No +,metric,CP.3.c.i,What is the level of coal rents in the country as a percentage of GDP?,% +,indicator,CP.3.d,Has the country committed not to approve new long lead time upstream oil and gas projects?,Yes/No +,metric,CP.3.d.i,What is the level of oil rents in the country as a percentage of GDP?,% +,metric,CP.3.d.ii,What is the level of natural gas rents in the country as a percentage of GDP?,% +,area,CP.4,Sectoral Transitions,Yes/No/Partial +,indicator,CP.4.a,Does the country have a long-term multi-sector climate strategy?,Yes/No +,indicator,CP.4.b,Does the country have a law and target on energy efficiency?,Yes/No +,metric,CP.4.b.i,What is the country's energy intensity of primary energy?,MJ/PPP-adjusted GDP +,indicator,CP.4.c,Has the country established mandatory reporting of climate-related disclosures?,Yes/No +,metric,CP.4.c.i,What share of the highest-emitting companies headquartered in the country have integrated climate change into their corporate strategy?,% +,metric,CP.4.c.ii,What share of the highest-emitting companies headquartered in this country have long-term targets aligned with 1.5°C?,% +,indicator,CP.4.d,Has the country set a net zero electricity target aligned with 1.5°C?,Yes/No +,metric,CP.4.d.i,What percentage of the country’s electricity generation is from low-carbon sources?,% +,indicator,CP.4.e,Has the country increased its protected areas as a percentage of total land area over the last 5 years?,Yes/No +,metric,CP.4.e.i,What is the amount of protected area in the country as a percentage of total land area?,% +,area,CP.5,Adaptation,Yes/No/Partial +,indicator,CP.5.a,Has the country published a National Adaptation Plan?,Yes/No +,indicator,CP.5.b,Does the country regularly publish national climate risk assessments?,Yes/No +,indicator,CP.5.c,Has the country published a Monitoring & Evaluation report on implementing adaptation?,Yes/No +,indicator,CP.5.d,Does the country have a multi-hazard early warning system?,Yes/No +,indicator,CP.5.e,Is the country part of a sovereign catastrophe risk pool?,Yes/No +,area,CP.6,Just Transition,Yes/No/Partial +,indicator,CP.6.a,"Has the country ratified fundamental human, labour, and Indigenous rights conventions? ",Yes/No +,metric,CP.6.a.i,At what percentile is the country's Voice and Accountability estimate?,% +,indicator,CP.6.b,Does the country have an inclusive and institutionalised approach on just transition?,Yes/No +,indicator,CP.6.c,Does the country have a green jobs strategy?,Yes/No +,indicator,CP.6.d,Does the country integrate just transition into its carbon pricing or fossil fuel subsidy reform?,Yes/No +,pillar,CF,Climate Finance, +,area,CF.1,International Climate Finance,Yes/No/Partial +,indicator,CF.1.a,Does the country contribute at least a proportional share of the $100 billion commitment to climate finance?,Yes/No +,metric,CF.1.a.i,What is the country’s 3-year average climate finance contributions as a % of GDP?,% +,indicator,CF.1.b,Has the country set a target for increasing its international climate finance contributions?,Yes/No +,metric,CF.1.b.i,What is the country’s targeted level of international climate finance contributions as a % of GDP?,% +,area,CF.2,Transparency of Climate Costing,Yes/No/Partial +,indicator,CF.2.a,Has the country disclosed a transparent breakdown of the costs of implementing its Nationally Determined Contribution?,Yes/No +,indicator,CF.2.b,Has the country disclosed a transparent breakdown of the costs of implementing its National Adaptation Plan?,Yes/No +,area,CF.3,Transparency of Climate Spending,Yes/No/Partial +,indicator,CF.3.a,Has the country disclosed its climate-related expenditures?,Yes/No +,indicator,CF.3.b,Has the country published a transparent climate budget tagging report?,Yes/No +,area,CF.4,Renewable Opportunities, +,metric,CF.4.i,What is the scale of the country's prospective solar energy capacity?,MW/GDP +,metric,CF.4.ii,What is the country’s prospective wind energy capacity?,MW/GDP +,metric,CF.4.iii,What is the country’s prospective geothermal energy capacity?,MW/GDP +,metric,CF.4.iv,What is the country’s prospective hydroelectric energy capacity?,MW/GDP \ No newline at end of file diff --git a/db/seeds/tpi/ascor_assessments.csv b/db/seeds/tpi/ascor_assessments.csv new file mode 100644 index 000000000..7d21eff78 --- /dev/null +++ b/db/seeds/tpi/ascor_assessments.csv @@ -0,0 +1,26 @@ +Id,Assessment Date,Publication Date,Country Id,Country,area EP.1,indicator EP.1.a,indicator EP.1.b,indicator EP.1.c,area EP.2,indicator EP.2.a,metric EP.2.a.i,indicator EP.2.b,metric EP.2.b.i,indicator EP.2.c,metric EP.2.c.i,indicator EP.2.d,metric EP.2.d.i,area EP.3,indicator EP.3.a,metric EP.3.a.i,indicator EP.3.b,indicator EP.3.c,area CP.1,indicator CP.1.a,indicator CP.1.b,area CP.2,indicator CP.2.a,indicator CP.2.b,metric CP.2.b.i,indicator CP.2.c,metric CP.2.c.i,area CP.3,indicator CP.3.a,metric CP.3.a.i,indicator CP.3.b,metric CP.3.b.i,metric CP.3.b.ii,indicator CP.3.c,metric CP.3.c.i,indicator CP.3.d,metric CP.3.d.i,metric CP.3.d.ii,area CP.4,indicator CP.4.a,indicator CP.4.b,metric CP.4.b.i,indicator CP.4.c,metric CP.4.c.i,metric CP.4.c.ii,indicator CP.4.d,metric CP.4.d.i,indicator CP.4.e,metric CP.4.e.i,area CP.5,indicator CP.5.a,indicator CP.5.b,indicator CP.5.c,indicator CP.5.d,indicator CP.5.e,area CP.6,indicator CP.6.a,metric CP.6.a.i,indicator CP.6.b,indicator CP.6.c,indicator CP.6.d,area CF.1,indicator CF.1.a,metric CF.1.a.i,indicator CF.1.b,metric CF.1.b.i,area CF.2,indicator CF.2.a,indicator CF.2.b,area CF.3,indicator CF.3.a,indicator CF.3.b,area CF.4,metric CF.4.i,metric CF.4.ii,metric CF.4.iii,metric CF.4.iv,source indicator EP.1.a,source metric EP.1.a.i,source metric EP.1.a.ii,source indicator EP.1.b,source indicator EP.1.c,source indicator EP.2.a,source metric EP.2.a.i,source indicator EP.2.b,source metric EP.2.b.i,source indicator EP.2.c,source metric EP.2.c.i,source indicator EP.2.d,source metric EP.2.d.i,source indicator EP.3.a,source metric EP.3.a.i,source indicator EP.3.b,source indicator EP.3.c,source indicator CP.1.a,source indicator CP.1.b,source indicator CP.2.a,source indicator CP.2.b,source metric CP.2.b.i,source indicator CP.2.c,source metric CP.2.c.i,source indicator CP.3.a,source metric CP.3.a.i,source indicator CP.3.b,source metric CP.3.b.i,source metric CP.3.b.ii,source indicator CP.3.c,source metric CP.3.c.i,source indicator CP.3.d,source metric CP.3.d.i,source metric CP.3.d.ii,source indicator CP.4.a,source indicator CP.4.b,source metric CP.4.b.i,source indicator CP.4.c,source metric CP.4.c.i,source metric CP.4.c.ii,source indicator CP.4.d,source metric CP.4.d.i,source indicator CP.4.e,source metric CP.4.e.i,source indicator CP.5.a,source indicator CP.5.b,source indicator CP.5.c,source indicator CP.5.d,source indicator CP.5.e,source indicator CP.6.a,source metric CP.6.a.i,source indicator CP.6.b,source indicator CP.6.c,source indicator CP.6.d,source indicator CF.1.a,source metric CF.1.a.i,source indicator CF.1.b,source metric CF.1.b.i,source indicator CF.2.a,source indicator CF.2.b,source indicator CF.3.a,source indicator CF.3.b,source metric CF.4.i,source metric CF.4.ii,source metric CF.4.iii,source metric CF.4.iv,year metric EP.1.a.i,year metric EP.1.a.ii,year metric EP.2.a.i,year metric EP.2.b.i,year metric EP.2.c.i,year metric EP.2.d.i,year metric EP.3.a.i,year metric CP.2.b.i,year metric CP.2.c.i,year metric CP.3.a.i,year metric CP.3.b.i,year metric CP.3.b.ii,year metric CP.3.c.i,year metric CP.3.d.i,year metric CP.3.d.ii,year metric CP.4.b.i,year metric CP.4.c.i,year metric CP.4.c.ii,year metric CP.4.d.i,year metric CP.4.e.i,year metric CP.6.a.i,year metric CF.1.a.i,year metric CF.1.b.i,year metric CF.4.i,year metric CF.4.ii,year metric CF.4.iii,year metric CF.4.iv +,10/30/23,2023-12,,United States,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727475,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Japan,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727476,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,France,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727477,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Italy,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727478,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Egypt,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727479,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Uruguay,Yes,Yes,No data,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727480,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Saudi Arabia,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727481,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Australia,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727482,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Mexico,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727483,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,China,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727484,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Poland,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727485,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Indonesia,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727486,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Kazakhstan,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727487,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Canada,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727488,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Thailand,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727489,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Chile,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727490,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,South Africa,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727491,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Brazil,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727492,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Kenya,Partial,No,No,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727493,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Morocco,Partial,No,No,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727494,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Germany,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727495,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,United Kingdom,Partial,Yes,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727496,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,India,No,No,No,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727497,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Barbados,Partial,No,No data,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727498,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,10/30/23,2023-12,,Bangladesh,Partial,No,No,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,https://zenodo.org/record/7727499,,,https://1p5ndc-pathways.climateanalytics.org/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/db/seeds/tpi/ascor_benchmarks.csv b/db/seeds/tpi/ascor_benchmarks.csv new file mode 100644 index 000000000..8ac1d2fb3 --- /dev/null +++ b/db/seeds/tpi/ascor_benchmarks.csv @@ -0,0 +1,51 @@ +Id,Country,Publication date,Emissions metric,Emissions boundary,Units,Benchmark type,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030 +,United States,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark," 6,220 "," 5,845 "," 5,470 "," 5,095 "," 4,720 "," 4,345 "," 3,970 "," 3,595 "," 3,219 "," 2,844 " +,Japan,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,France,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Italy,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Egypt,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Uruguay,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Saudi Arabia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Australia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Mexico,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,China,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Poland,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Indonesia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Kazakhstan,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Canada,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Thailand,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Chile,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,South Africa,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Brazil,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Kenya,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Morocco,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Germany,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,United Kingdom,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,India,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Barbados,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,Bangladesh,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark,,,,,,,,,, +,United States,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation," 6,220 "," 5,576 "," 4,932 "," 4,288 "," 3,644 "," 3,000 "," 2,356 "," 1,712 "," 1,068 ", 425 +,Japan,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,France,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Italy,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Egypt,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Uruguay,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Saudi Arabia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Australia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Mexico,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,China,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Poland,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Indonesia,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Kazakhstan,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Canada,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Thailand,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Chile,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,South Africa,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Brazil,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Kenya,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Morocco,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Germany,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,United Kingdom,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,India,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Barbados,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, +,Bangladesh,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,Fair share 1.5C allocation,,,,,,,,,, \ No newline at end of file diff --git a/db/seeds/tpi/ascor_countries.csv b/db/seeds/tpi/ascor_countries.csv new file mode 100644 index 000000000..df4c41472 --- /dev/null +++ b/db/seeds/tpi/ascor_countries.csv @@ -0,0 +1,26 @@ +Id,Name,Country ISO code,Region,World Bank lending group,International Monetary Fund fiscal monitor category,Type of Party to the United Nations Framework Convention on Climate Change +,United States,USA,North America,High-income economies,Advanced economies,Annex I +,Japan,JPN,Asia,High-income economies,Advanced economies,Annex I +,France,FRA,Europe,High-income economies,Advanced economies,Annex I +,Italy,ITA,Europe,High-income economies,Advanced economies,Annex I +,Egypt,EGY,Africa,Lower-middle-income economies,Emerging market economies,Non-Annex I +,Uruguay,URY,Latin America and Caribbean,High-income economies,Emerging market economies,Non-Annex I +,Saudi Arabia,SAU,Asia,High-income economies,Emerging market economies,Non-Annex I +,Australia,AUS,Oceania,High-income economies,Advanced economies,Annex I +,Mexico,MEX,Latin America and Caribbean,Upper-middle-income economies,Emerging market economies,Non-Annex I +,China,CHN,Asia,Upper-middle-income economies,Emerging market economies,Non-Annex I +,Poland,POL,Europe,High-income economies,Emerging market economies,Annex I +,Indonesia,IDN,Asia,Lower-middle-income economies,Emerging market economies,Non-Annex I +,Kazakhstan,KAZ,Asia,Upper-middle-income economies,Emerging market economies,Non-Annex I +,Canada,CAN,North America,High-income economies,Advanced economies,Annex I +,Thailand,THA,Asia,Upper-middle-income economies,Emerging market economies,Non-Annex I +,Chile,CHL,Latin America and Caribbean,High-income economies,Emerging market economies,Non-Annex I +,South Africa,ZAF,Africa,Upper-middle-income economies,Emerging market economies,Non-Annex I +,Brazil,BRA,Latin America and Caribbean,Upper-middle-income economies,Emerging market economies,Non-Annex I +,Kenya,KEN,Africa,Lower-middle-income economies,Low-income developing countries,Non-Annex I +,Morocco,MAR,Africa,Lower-middle-income economies,Emerging market economies,Non-Annex I +,Germany,DEU,Europe,High-income economies,Advanced economies,Annex I +,United Kingdom,GBR,Europe,High-income economies,Advanced economies,Annex I +,India,IND,Asia,Lower-middle-income economies,Emerging market economies,Non-Annex I +,Barbados,BRB,Latin America and Caribbean,High-income economies,Emerging market economies,Non-Annex I +,Bangladesh,BGD,Asia,Lower-middle-income economies,Low-income developing countries,Non-Annex I \ No newline at end of file diff --git a/db/seeds/tpi/ascor_pathways.csv b/db/seeds/tpi/ascor_pathways.csv new file mode 100644 index 000000000..b39057778 --- /dev/null +++ b/db/seeds/tpi/ascor_pathways.csv @@ -0,0 +1,226 @@ +Id,Country,Emissions metric,Emissions boundary,Units,Assessment date,Publication date,Last historical year,metric EP1.a.i,source metric EP1.a.i,year metric EP1.a.i,metric EP1.a.ii 1-year,metric EP1.a.ii 3-year,metric EP1.a.ii 5-year,source metric EP1.a.ii,year metric EP1.a.ii,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030 +,United States,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,2021," 6,220.0 ",https://zenodo.org/record/7727475,2021,3.9%,-2.3%,-0.9%,https://zenodo.org/record/7727475,2021,"7,460.0","7,380.0","7,480.0","7,260.0","6,800.0","7,020.0","6,860.0","6,620.0","6,800.0","6,860.0","6,700.0","6,550.0","6,510.0","6,700.0","6,580.0","5,990.0","6,220.0","5,981.5","5,743.0","5,504.5","5,266.0","5,027.5","4,789.0","4,550.4","4,311.9","4,073.4" +,United States,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,2021, 18.7 ,https://zenodo.org/record/7727476,2021,3.8%,-2.8%,-1.4%,https://zenodo.org/record/7727476,2021,NA,NA,NA,NA,NA,NA,NA,21.1,21.5,21.5,20.9,20.3,20.0,20.5,20.0,18.1,18.7,,,,,,,,, +,United States,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,2021, 266.8 ,https://zenodo.org/record/7727477,2021,-6.1%,-6.5%,-5.3%,https://zenodo.org/record/7727477,2021,572.1,534.2,516.8,491.5,469.7,466.5,439.8,407.3,403.7,390.9,368.0,350.4,334.2,326.4,307.9,286.7,270.5,,,,,,,,, +,United States,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,2018,- 792.3 ,https://zenodo.org/record/7727481,2021,0.6%,0.2%,1.4%,https://zenodo.org/record/7727481,2021,"6,799.2","6,732.2","6,664.5","6,340.3","5,822.1","6,070.3","5,836.8","5,628.0","5,732.7","5,759.1","5,739.0","5,635.7","5,595.1","5,740.7",,,,,,,,,,,, +,United States,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,2018,- 2.4 ,https://zenodo.org/record/7727482,2021,0.8%,0.8%,2.0%,https://zenodo.org/record/7727482,2021,NA,NA,NA,NA,NA,NA,NA,17.9,18.1,18.1,17.9,17.4,17.2,17.6,,,,,,,,,,,, +,United States,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,2018,- 34.0 ,https://zenodo.org/record/7727483,2021,10.2%,4.1%,5.6%,https://zenodo.org/record/7727483,2021,521.4,487.3,460.4,429.3,402.1,403.4,374.2,346.3,340.4,328.1,315.2,301.5,287.2,279.7,,,,,,,,,,,, +,United States,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,2021," 5,740.7 ",https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2021,2018,2.6%,0.0%,0.0%,https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2021,2018,-820.7,-846.2,-807.9,-794.5,-763.6,-786.5,-828.6,-829.2,-796.7,-808.2,-736.1,-854.9,-815.2,-799.9,-756.0,-797.0,-792.3,,,,,,,,, +,United States,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,2021, 17.6 ,https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2022,2018,2.1%,-0.6%,-0.6%,https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2022,2018,NA,NA,NA,NA,NA,NA,NA,-2.6,-2.5,-2.5,-2.3,-2.6,-2.5,-2.4,-2.3,-2.4,-2.4,,,,,,,,, +,United States,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,2021, 279.6 ,https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2023,2018,-2.7%,-3.9%,-3.9%,https://stats.oecd.org/Index.aspx?DataSetCode=IO_GHG_2023,2018,-62.9,-61.3,-55.8,-53.8,-52.7,-52.3,-53.1,-51.0,-47.3,-46.1,-40.4,-45.7,-41.8,-39.0,-35.4,-38.1,-34.5,,,,,,,,, +,Japan,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Japan,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,France,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Italy,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Egypt,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Uruguay,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Saudi Arabia,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Australia,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Mexico,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,China,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Poland,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Indonesia,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kazakhstan,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Canada,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Thailand,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Chile,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,South Africa,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Brazil,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Kenya,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Morocco,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Germany,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,United Kingdom,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,India,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Barbados,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per capita,Production - excluding LULUCF,tCO2e/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per GDP-PPP,Production - excluding LULUCF,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Absolute,Consumption - excluding LULUCF,MtCO2,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per capita,Consumption - excluding LULUCF,tCO2/capita,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Absolute,Production - only LULUCF ,MtCO2e,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per capita,Production - only LULUCF ,tCO2e/Population,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Bangladesh,Intensity per GDP-PPP,Production - only LULUCF ,tCO2e/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index baf869227..fc0008d5d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -310,6 +310,229 @@ CREATE TABLE public.ar_internal_metadata ( ); +-- +-- Name: ascor_assessment_indicators; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_assessment_indicators ( + id bigint NOT NULL, + indicator_type character varying, + code character varying, + text text, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + units_or_response_type character varying +); + + +-- +-- Name: ascor_assessment_indicators_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_assessment_indicators_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_assessment_indicators_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_assessment_indicators_id_seq OWNED BY public.ascor_assessment_indicators.id; + + +-- +-- Name: ascor_assessment_results; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_assessment_results ( + id bigint NOT NULL, + assessment_id bigint NOT NULL, + indicator_id bigint NOT NULL, + answer character varying, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + source character varying, + year integer +); + + +-- +-- Name: ascor_assessment_results_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_assessment_results_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_assessment_results_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_assessment_results_id_seq OWNED BY public.ascor_assessment_results.id; + + +-- +-- Name: ascor_assessments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_assessments ( + id bigint NOT NULL, + country_id bigint NOT NULL, + assessment_date date, + publication_date date, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + notes text +); + + +-- +-- Name: ascor_assessments_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_assessments_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_assessments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_assessments_id_seq OWNED BY public.ascor_assessments.id; + + +-- +-- Name: ascor_benchmarks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_benchmarks ( + id bigint NOT NULL, + country_id bigint NOT NULL, + publication_date date, + emissions_metric character varying, + emissions_boundary character varying, + units character varying, + benchmark_type character varying, + emissions jsonb DEFAULT '{}'::jsonb, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: ascor_benchmarks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_benchmarks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_benchmarks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_benchmarks_id_seq OWNED BY public.ascor_benchmarks.id; + + +-- +-- Name: ascor_countries; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_countries ( + id bigint NOT NULL, + name character varying, + slug character varying, + iso character varying, + region character varying, + wb_lending_group character varying, + fiscal_monitor_category character varying, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + type_of_party character varying +); + + +-- +-- Name: ascor_countries_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_countries_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_countries_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_countries_id_seq OWNED BY public.ascor_countries.id; + + +-- +-- Name: ascor_pathways; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ascor_pathways ( + id bigint NOT NULL, + country_id bigint NOT NULL, + emissions_metric character varying, + emissions_boundary character varying, + units character varying, + assessment_date date, + publication_date date, + last_historical_year integer, + trend_1_year character varying, + trend_3_year character varying, + trend_5_year character varying, + emissions jsonb DEFAULT '{}'::jsonb, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + trend_source character varying, + trend_year integer, + recent_emission_level double precision, + recent_emission_source character varying, + recent_emission_year integer +); + + +-- +-- Name: ascor_pathways_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ascor_pathways_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ascor_pathways_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ascor_pathways_id_seq OWNED BY public.ascor_pathways.id; + + -- -- Name: bank_assessment_indicators; Type: TABLE; Schema: public; Owner: - -- @@ -1769,6 +1992,48 @@ ALTER TABLE ONLY public.activities ALTER COLUMN id SET DEFAULT nextval('public.a ALTER TABLE ONLY public.admin_users ALTER COLUMN id SET DEFAULT nextval('public.admin_users_id_seq'::regclass); +-- +-- Name: ascor_assessment_indicators id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_indicators ALTER COLUMN id SET DEFAULT nextval('public.ascor_assessment_indicators_id_seq'::regclass); + + +-- +-- Name: ascor_assessment_results id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_results ALTER COLUMN id SET DEFAULT nextval('public.ascor_assessment_results_id_seq'::regclass); + + +-- +-- Name: ascor_assessments id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessments ALTER COLUMN id SET DEFAULT nextval('public.ascor_assessments_id_seq'::regclass); + + +-- +-- Name: ascor_benchmarks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_benchmarks ALTER COLUMN id SET DEFAULT nextval('public.ascor_benchmarks_id_seq'::regclass); + + +-- +-- Name: ascor_countries id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_countries ALTER COLUMN id SET DEFAULT nextval('public.ascor_countries_id_seq'::regclass); + + +-- +-- Name: ascor_pathways id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_pathways ALTER COLUMN id SET DEFAULT nextval('public.ascor_pathways_id_seq'::regclass); + + -- -- Name: bank_assessment_indicators id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2084,6 +2349,54 @@ ALTER TABLE ONLY public.ar_internal_metadata ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key); +-- +-- Name: ascor_assessment_indicators ascor_assessment_indicators_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_indicators + ADD CONSTRAINT ascor_assessment_indicators_pkey PRIMARY KEY (id); + + +-- +-- Name: ascor_assessment_results ascor_assessment_results_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_results + ADD CONSTRAINT ascor_assessment_results_pkey PRIMARY KEY (id); + + +-- +-- Name: ascor_assessments ascor_assessments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessments + ADD CONSTRAINT ascor_assessments_pkey PRIMARY KEY (id); + + +-- +-- Name: ascor_benchmarks ascor_benchmarks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_benchmarks + ADD CONSTRAINT ascor_benchmarks_pkey PRIMARY KEY (id); + + +-- +-- Name: ascor_countries ascor_countries_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_countries + ADD CONSTRAINT ascor_countries_pkey PRIMARY KEY (id); + + +-- +-- Name: ascor_pathways ascor_pathways_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_pathways + ADD CONSTRAINT ascor_pathways_pkey PRIMARY KEY (id); + + -- -- Name: bank_assessment_indicators bank_assessment_indicators_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2388,6 +2701,13 @@ ALTER TABLE ONLY public.tpi_sectors ADD CONSTRAINT tpi_sectors_pkey PRIMARY KEY (id); +-- +-- Name: assessment_results_on_assessment_id_and_indicator_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX assessment_results_on_assessment_id_and_indicator_id ON public.ascor_assessment_results USING btree (assessment_id, indicator_id); + + -- -- Name: index_active_admin_comments_on_author_type_and_author_id; Type: INDEX; Schema: public; Owner: - -- @@ -2493,6 +2813,62 @@ CREATE UNIQUE INDEX index_admin_users_on_email ON public.admin_users USING btree CREATE UNIQUE INDEX index_admin_users_on_reset_password_token ON public.admin_users USING btree (reset_password_token); +-- +-- Name: index_ascor_assessment_results_on_assessment_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_ascor_assessment_results_on_assessment_id ON public.ascor_assessment_results USING btree (assessment_id); + + +-- +-- Name: index_ascor_assessment_results_on_indicator_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_ascor_assessment_results_on_indicator_id ON public.ascor_assessment_results USING btree (indicator_id); + + +-- +-- Name: index_ascor_assessments_on_country_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_ascor_assessments_on_country_id ON public.ascor_assessments USING btree (country_id); + + +-- +-- Name: index_ascor_benchmarks_on_country_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_ascor_benchmarks_on_country_id ON public.ascor_benchmarks USING btree (country_id); + + +-- +-- Name: index_ascor_countries_on_iso; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_ascor_countries_on_iso ON public.ascor_countries USING btree (iso); + + +-- +-- Name: index_ascor_countries_on_name; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_ascor_countries_on_name ON public.ascor_countries USING btree (name); + + +-- +-- Name: index_ascor_countries_on_slug; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_ascor_countries_on_slug ON public.ascor_countries USING btree (slug); + + +-- +-- Name: index_ascor_pathways_on_country_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_ascor_pathways_on_country_id ON public.ascor_pathways USING btree (country_id); + + -- -- Name: index_bank_assessment_indicators_on_indicator_type_and_number; Type: INDEX; Schema: public; Owner: - -- @@ -3275,6 +3651,14 @@ ALTER TABLE ONLY public.companies ADD CONSTRAINT fk_rails_1b3e78a93d FOREIGN KEY (sector_id) REFERENCES public.tpi_sectors(id); +-- +-- Name: ascor_benchmarks fk_rails_1b5ff247ca; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_benchmarks + ADD CONSTRAINT fk_rails_1b5ff247ca FOREIGN KEY (country_id) REFERENCES public.ascor_countries(id); + + -- -- Name: targets fk_rails_1bc68932f6; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3339,6 +3723,14 @@ ALTER TABLE ONLY public.litigations ADD CONSTRAINT fk_rails_3ad3738b8b FOREIGN KEY (geography_id) REFERENCES public.geographies(id) ON DELETE CASCADE; +-- +-- Name: ascor_pathways fk_rails_3d14118f90; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_pathways + ADD CONSTRAINT fk_rails_3d14118f90 FOREIGN KEY (country_id) REFERENCES public.ascor_countries(id); + + -- -- Name: images fk_rails_3ddaef631e; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3419,6 +3811,14 @@ ALTER TABLE ONLY public.litigation_sides ADD CONSTRAINT fk_rails_5ff60f0b08 FOREIGN KEY (litigation_id) REFERENCES public.litigations(id) ON DELETE CASCADE; +-- +-- Name: ascor_assessment_results fk_rails_636219043a; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_results + ADD CONSTRAINT fk_rails_636219043a FOREIGN KEY (assessment_id) REFERENCES public.ascor_assessments(id); + + -- -- Name: bank_assessments fk_rails_68427d4c57; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3523,6 +3923,14 @@ ALTER TABLE ONLY public.taggings ADD CONSTRAINT fk_rails_9fcd2e236b FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE; +-- +-- Name: ascor_assessments fk_rails_a3a5356523; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessments + ADD CONSTRAINT fk_rails_a3a5356523 FOREIGN KEY (country_id) REFERENCES public.ascor_countries(id); + + -- -- Name: publications fk_rails_a957b2faea; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3579,6 +3987,14 @@ ALTER TABLE ONLY public.data_uploads ADD CONSTRAINT fk_rails_e965439ee4 FOREIGN KEY (uploaded_by_id) REFERENCES public.admin_users(id); +-- +-- Name: ascor_assessment_results fk_rails_fee6a2c7af; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ascor_assessment_results + ADD CONSTRAINT fk_rails_fee6a2c7af FOREIGN KEY (indicator_id) REFERENCES public.ascor_assessment_indicators(id); + + -- -- PostgreSQL database dump complete -- @@ -3736,6 +4152,13 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230622093001'), ('20230712074753'), ('20230713121501'), +('20230912074824'), +('20230912103652'), +('20230914065509'), +('20230914100201'), +('20230915063557'), +('20230915064402'), +('20230920083300'), ('20230926075145'), ('20230927112905'), ('20231023101859'), diff --git a/db/test-dump.psql b/db/test-dump.psql index 5f75e2365..9f333dd26 100644 Binary files a/db/test-dump.psql and b/db/test-dump.psql differ diff --git a/lib/tasks/onetime.rake b/lib/tasks/onetime.rake index d246a231b..755605496 100644 --- a/lib/tasks/onetime.rake +++ b/lib/tasks/onetime.rake @@ -110,6 +110,19 @@ class OneTimeTasks text: '107' ) end + + tpi_ascor_page = TPIPage.create_with(menu: :no_menu_entry).find_or_create_by!(title: 'ASCOR', slug: 'ascor') + tpi_ascor_page.contents.destroy_all + tpi_ascor_page.contents.create!( + title: 'Methodology', + code: 'methodology_description', + text: '
    TODO
    ' + ) + tpi_ascor_page.contents.create!( + title: 'Methodology Publication ID', + code: 'methodology_publication_id', + text: 'TODO' + ) end end end diff --git a/spec/commands/csv_data_upload_spec.rb b/spec/commands/csv_data_upload_spec.rb index c74a5832e..9967343b6 100644 --- a/spec/commands/csv_data_upload_spec.rb +++ b/spec/commands/csv_data_upload_spec.rb @@ -13,6 +13,11 @@ let(:bank_cp_assessments_csv) { fixture_file('bank_cp_assessments.csv') } let(:mq_assessments_csv) { fixture_file('mq_assessments.csv') } let(:geographies_csv) { fixture_file('geographies.csv') } + let(:ascor_countries_csv) { fixture_file('ascor_countries.csv') } + let(:ascor_benchmarks_csv) { fixture_file('ascor_benchmarks.csv') } + let(:ascor_pathways_csv) { fixture_file('ascor_pathways.csv') } + let(:ascor_assessment_indicators_csv) { fixture_file('ascor_assessment_indicators.csv') } + let(:ascor_assessments_csv) { fixture_file('ascor_assessments.csv') } let(:current_user_role) { 'super_user' } let(:current_user) { build(:admin_user, role: current_user_role) } let_it_be(:countries) do @@ -23,6 +28,11 @@ create(:geography, iso: 'USA', name: 'United States') ] end + let_it_be(:ascor_countries) do + [ + create(:ascor_country, iso: 'USA', name: 'United States') + ] + end before do ::Current.admin_user = current_user @@ -419,6 +429,91 @@ expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) end + it 'for ASCOR countries' do + csv_content = <<-CSV + Name + New Name + CSV + + command = expect_data_upload_results( + ASCOR::Country, + fixture_file('ascor_countries.csv', content: csv_content), + {new_records: 0, not_changed_records: 0, rows: 1, updated_records: 0}, + expected_success: false, + custom_uploader: 'ASCORCountries' + ) + + expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) + end + + it 'for ASCOR benchmarks' do + csv_content = <<-CSV + Country,Publication Date + New Country,2020-01 + CSV + + command = expect_data_upload_results( + ASCOR::Benchmark, + fixture_file('ascor_benchmarks.csv', content: csv_content), + {new_records: 0, not_changed_records: 0, rows: 1, updated_records: 0}, + expected_success: false, + custom_uploader: 'ASCORBenchmarks' + ) + + expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) + end + + it 'for ASCOR pathways' do + csv_content = <<-CSV + Country,Publication Date + New Country,2020-01 + CSV + + command = expect_data_upload_results( + ASCOR::Pathway, + fixture_file('ascor_pathways.csv', content: csv_content), + {new_records: 0, not_changed_records: 0, rows: 1, updated_records: 0}, + expected_success: false, + custom_uploader: 'ASCORPathways' + ) + + expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) + end + + it 'for ASCOR assessment indicator' do + csv_content = <<-CSV + Code,Type,Text + EP,indicator,Test + CSV + + command = expect_data_upload_results( + ASCOR::AssessmentIndicator, + fixture_file('ascor_assessment_indicators.csv', content: csv_content), + {new_records: 0, not_changed_records: 0, rows: 1, updated_records: 0}, + expected_success: false, + custom_uploader: 'ASCORAssessmentIndicators' + ) + + expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) + end + + it 'for ASCOR assessments' do + csv_content = <<-CSV + Country,Publication Date + New Country,2020-01 + CSV + + command = expect_data_upload_results( + ASCOR::Assessment, + fixture_file('ascor_assessments.csv', content: csv_content), + {new_records: 0, not_changed_records: 0, rows: 1, updated_records: 0}, + expected_success: false, + custom_uploader: 'ASCORAssessments' + ) + + expect(command.errors.messages[:base]).to eq(['CSV missing header: Id']) + end + it 'for Companies' do csv_content = <<-CSV Name @@ -1358,6 +1453,186 @@ expect(geography.federal_details).to be end + it 'import CSV file with ASCOR countries data' do + # USA is already created + expect_data_upload_results( + ASCOR::Country, + ascor_countries_csv, + {new_records: 1, not_changed_records: 1, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORCountries' + ) + # subsequent import should not create or update any record + expect_data_upload_results( + ASCOR::Country, + ascor_countries_csv, + {new_records: 0, not_changed_records: 2, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORCountries' + ) + + country = ASCOR::Country.find_by(iso: 'JPN') + expect(country.name).to eq('Japan') + expect(country.region).to eq('Asia') + expect(country.wb_lending_group).to eq('High-income economies') + expect(country.fiscal_monitor_category).to eq('Advanced economies') + expect(country.type_of_party).to eq('Annex I') + end + + it 'import CSV file with ASCOR benchmarks data' do + create(:ascor_country, iso: 'JPN', name: 'Japan') + + expect_data_upload_results( + ASCOR::Benchmark, + ascor_benchmarks_csv, + {new_records: 2, not_changed_records: 0, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORBenchmarks' + ) + # subsequent import should not create or update any record + expect_data_upload_results( + ASCOR::Benchmark, + ascor_benchmarks_csv, + {new_records: 0, not_changed_records: 2, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORBenchmarks' + ) + + benchmark = ASCOR::Benchmark.joins(:country).find_by ascor_countries: {iso: 'JPN'} + + expect(benchmark.publication_date).to eq(Date.new(2023, 12)) + expect(benchmark.emissions_metric).to eq('Absolute') + expect(benchmark.emissions_boundary).to eq('Production - excluding LULUCF') + expect(benchmark.units).to eq('MtCO2e') + expect(benchmark.benchmark_type).to eq('National 1.5C benchmark') + expect(benchmark.emissions).to eq( + '2021' => 6220.0, + '2022' => 5845.0, + '2023' => 5470.0, + '2024' => 5095.0, + '2025' => 4720.0, + '2026' => 4345.0, + '2027' => 3970.0, + '2028' => 3595.0, + '2029' => 3219.0, + '2030' => 2844.0 + ) + end + + it 'import CSV file with ASCOR pathways data' do + create(:ascor_country, iso: 'JPN', name: 'Japan') + + expect_data_upload_results( + ASCOR::Pathway, + ascor_pathways_csv, + {new_records: 2, not_changed_records: 0, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORPathways' + ) + # subsequent import should not create or update any record + expect_data_upload_results( + ASCOR::Benchmark, + ascor_pathways_csv, + {new_records: 0, not_changed_records: 2, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORPathways' + ) + + pathway = ASCOR::Pathway.joins(:country).find_by ascor_countries: {iso: 'USA'} + + expect(pathway.publication_date).to eq(Date.new(2023, 12)) + expect(pathway.assessment_date).to eq(Date.new(2023, 10, 30)) + expect(pathway.emissions_metric).to eq('Absolute') + expect(pathway.emissions_boundary).to eq('Production - excluding LULUCF') + expect(pathway.units).to eq('MtCO2e') + expect(pathway.trend_1_year).to eq('3.9%') + expect(pathway.trend_3_year).to eq('-2.3%') + expect(pathway.trend_5_year).to eq('-0.9%') + expect(pathway.trend_source).to eq('https://zenodo.org/record/7727475') + expect(pathway.trend_year).to eq(2021) + expect(pathway.recent_emission_level).to eq(-792.3) + expect(pathway.recent_emission_source).to eq('https://zenodo.org/record/7727475') + expect(pathway.recent_emission_year).to eq(2021) + expect(pathway.emissions).to eq( + '2006' => 7380.0, + '2007' => 7480.0, + '2008' => 7260.0, + '2009' => 6800.0, + '2010' => 7020.0, + '2011' => 6860.0, + '2012' => 6620.0, + '2013' => 6800.0, + '2014' => 6860.0, + '2015' => 6700.0, + '2016' => 6550.0, + '2017' => 6510.0, + '2018' => 6700.0, + '2019' => 6580.0, + '2020' => 5990.0, + '2021' => 6220.0, + '2022' => 5981.5, + '2023' => 5743.0, + '2024' => 5504.5, + '2025' => 5266.0, + '2026' => 5027.5, + '2027' => 4789.0, + '2028' => 4550.4, + '2029' => 4311.9, + '2030' => 4073.4 + ) + end + + it 'import CSV file with ASCOR assessment indicators data' do + expect_data_upload_results( + ASCOR::AssessmentIndicator, + ascor_assessment_indicators_csv, + {new_records: 7, not_changed_records: 0, rows: 7, updated_records: 0}, + custom_uploader: 'ASCORAssessmentIndicators' + ) + # subsequent import should not create or update any record + expect_data_upload_results( + ASCOR::Benchmark, + ascor_assessment_indicators_csv, + {new_records: 0, not_changed_records: 7, rows: 7, updated_records: 0}, + custom_uploader: 'ASCORAssessmentIndicators' + ) + + indicator = ASCOR::AssessmentIndicator.find_by code: 'EP.1' + expect(indicator.indicator_type).to eq('area') + expect(indicator.text).to eq('Emissions Trends') + expect(indicator.units_or_response_type).to eq('Yes/No/Partial') + end + + it 'import CSV file with ASCOR assessments data' do + create :ascor_country, iso: 'JPN', name: 'Japan' + create :ascor_assessment_indicator, code: 'EP.1.a', indicator_type: 'indicator', + text: 'Have the country’s emissions decreased in recent years?' + create :ascor_assessment_indicator, code: 'EP.1.a.i', indicator_type: 'metric', + text: "What is the country's most recent emissions level?" + + expect_data_upload_results( + ASCOR::Assessment, + ascor_assessments_csv, + {new_records: 2, not_changed_records: 0, rows: 2, updated_records: 0}, + custom_uploader: 'ASCORAssessments' + ) + # subsequent import should not create or update any record + expect_data_upload_results( + ASCOR::Assessment, + ascor_assessments_csv, + {new_records: 0, not_changed_records: 0, rows: 2, updated_records: 2}, + custom_uploader: 'ASCORAssessments' + ) + + assessment = ASCOR::Assessment.joins(:country).find_by ascor_countries: {iso: 'JPN'} + indicator_1 = assessment.results.joins(:indicator).find_by ascor_assessment_indicators: {code: 'EP.1.a'} + indicator_2 = assessment.results.joins(:indicator).find_by ascor_assessment_indicators: {code: 'EP.1.a.i'} + + expect(assessment.notes).to be_nil + expect(assessment.publication_date).to eq(Date.new(2023, 12)) + expect(assessment.assessment_date).to eq(Date.new(2023, 10, 30)) + expect(indicator_1.answer).to eq('Yes') + expect(indicator_2.answer).to be_nil + expect(indicator_1.source).to eq('https://zenodo.org/record/7727476') + expect(indicator_2.source).to be_nil + expect(indicator_1.year).to be_nil + expect(indicator_2.year).to eq(2010) + end + def expect_data_upload_results(uploaded_resource_klass, csv, expected_details, expected_success: true, custom_uploader: nil) uploader_name = custom_uploader || uploaded_resource_klass.name.tr('::', '').pluralize command = Command::CSVDataUpload.new(uploader: uploader_name, file: csv) diff --git a/spec/controllers/admin/ascor_assessment_indicators_controller_spec.rb b/spec/controllers/admin/ascor_assessment_indicators_controller_spec.rb new file mode 100644 index 000000000..5b4bd6d42 --- /dev/null +++ b/spec/controllers/admin/ascor_assessment_indicators_controller_spec.rb @@ -0,0 +1,64 @@ +require 'rails_helper' + +RSpec.describe Admin::ASCORAssessmentIndicatorsController, type: :controller do + let(:admin) { create(:admin_user) } + let_it_be(:ascor_assessment_indicator) { create(:ascor_assessment_indicator) } + let(:valid_attributes) { attributes_for(:ascor_assessment_indicator).merge code: 'TEST' } + let(:invalid_attributes) { valid_attributes.merge code: nil } + + before { sign_in admin } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_assessment_indicator.id} } + + it { is_expected.to be_successful } + end + + describe 'GET edit' do + subject { get :edit, params: {id: ascor_assessment_indicator.id} } + + it { is_expected.to be_successful } + end + + describe 'PUT update' do + context 'with valid params' do + subject { put :update, params: {id: ascor_assessment_indicator.id, ascor_assessment_indicator: valid_attributes} } + + it 'updates the requested ASCOR Assessment Indicator' do + expect { subject }.to change { ascor_assessment_indicator.reload.code }.to(valid_attributes[:code]) + end + + it 'redirects to the ASCOR Assessment Indicator' do + expect(subject).to redirect_to(admin_ascor_assessment_indicator_path(ascor_assessment_indicator)) + end + end + + context 'with invalid params' do + subject { put :update, params: {id: ascor_assessment_indicator.id, ascor_assessment_indicator: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not update the ASCOR Assessment Indicator' do + expect { subject }.not_to change(ascor_assessment_indicator.reload, :code) + end + end + end + + describe 'DELETE destroy' do + subject { delete :destroy, params: {id: ascor_assessment_indicator.id} } + + it 'destroys the requested ASCOR Assessment Indicator' do + expect { subject }.to change(ASCOR::AssessmentIndicator, :count).by(-1) + end + + it 'redirects to the ASCOR Assessment Indicators list' do + expect(subject).to redirect_to(admin_ascor_assessment_indicators_path) + end + end +end diff --git a/spec/controllers/admin/ascor_assessments_controller_spec.rb b/spec/controllers/admin/ascor_assessments_controller_spec.rb new file mode 100644 index 000000000..9df1a2b9a --- /dev/null +++ b/spec/controllers/admin/ascor_assessments_controller_spec.rb @@ -0,0 +1,97 @@ +require 'rails_helper' + +RSpec.describe Admin::ASCORAssessmentsController, type: :controller do + let(:admin) { create(:admin_user) } + let_it_be(:ascor_country) { create :ascor_country, name: 'TEST', iso: 'TEST' } + let_it_be(:ascor_assessment_result) { create(:ascor_assessment_result) } + let_it_be(:ascor_assessment) { ascor_assessment_result.assessment } + let(:valid_attributes) do + attributes_for(:ascor_assessment).merge( + country_id: ascor_country.id, + results_attributes: { + '0' => attributes_for(:ascor_assessment_result).merge( + indicator_id: ascor_assessment_result.indicator_id + ) + } + ) + end + let(:invalid_attributes) { valid_attributes.merge country_id: nil } + + before { sign_in admin } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_assessment.id} } + + it { is_expected.to be_successful } + end + + describe 'GET new' do + subject { get :new } + + it { is_expected.to be_successful } + end + + describe 'GET edit' do + subject { get :edit, params: {id: ascor_assessment.id} } + + it { is_expected.to be_successful } + end + + describe 'POST create' do + context 'with valid params' do + subject { post :create, params: {ascor_assessment: valid_attributes} } + + it 'creates a new ASCOR Assessment' do + expect { subject }.to change(ASCOR::Assessment, :count).by(1) + end + + it 'redirects to the created Assessment' do + expect(subject).to redirect_to(admin_ascor_assessment_path(ASCOR::Assessment.order(:created_at).last)) + end + end + + context 'with invalid params' do + subject { post :create, params: {ascor_assessment: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not create a ASCOR Assessment' do + expect { subject }.not_to change(ASCOR::Assessment, :count) + end + end + end + + describe 'PUT update' do + context 'with valid params' do + subject { put :update, params: {id: ascor_assessment.id, ascor_assessment: valid_attributes} } + + before do + valid_attributes[:results_attributes]['0'][:id] = ascor_assessment_result.id + end + + it 'updates the requested assessment' do + expect { subject }.to change { ascor_assessment.reload.country_id }.to(ascor_country.id) + end + + it 'redirects to the assessment' do + expect(subject).to redirect_to(admin_ascor_assessment_path(ascor_assessment)) + end + end + + context 'with invalid params' do + subject { put :update, params: {id: ascor_assessment.id, ascor_assessment: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not update the requested benchmark' do + expect { subject }.not_to(change { ascor_assessment.reload.country_id }) + end + end + end +end diff --git a/spec/controllers/admin/ascor_benchmarks_controller_spec.rb b/spec/controllers/admin/ascor_benchmarks_controller_spec.rb new file mode 100644 index 000000000..ddaa20ebf --- /dev/null +++ b/spec/controllers/admin/ascor_benchmarks_controller_spec.rb @@ -0,0 +1,95 @@ +require 'rails_helper' + +RSpec.describe Admin::ASCORBenchmarksController, type: :controller do + let(:admin) { create(:admin_user) } + let_it_be(:ascor_country) { create :ascor_country, name: 'TEST', iso: 'TEST' } + let_it_be(:ascor_benchmark) { create(:ascor_benchmark) } + let(:valid_attributes) { attributes_for(:ascor_benchmark).merge country_id: ascor_country.id } + let(:invalid_attributes) { valid_attributes.merge country_id: nil } + + before { sign_in admin } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_benchmark.id} } + + it { is_expected.to be_successful } + end + + describe 'GET new' do + subject { get :new } + + it { is_expected.to be_successful } + end + + describe 'GET edit' do + subject { get :edit, params: {id: ascor_benchmark.id} } + + it { is_expected.to be_successful } + end + + describe 'POST create' do + context 'with valid params' do + subject { post :create, params: {ascor_benchmark: valid_attributes} } + + it 'creates a new ASCOR Benchmark' do + expect { subject }.to change(ASCOR::Benchmark, :count).by(1) + end + + it 'redirects to the created Benchmark' do + expect(subject).to redirect_to(admin_ascor_benchmark_path(ASCOR::Benchmark.order(:created_at).last)) + end + end + + context 'with invalid params' do + subject { post :create, params: {ascor_benchmark: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not create a ASCOR Benchmark' do + expect { subject }.not_to change(ASCOR::Benchmark, :count) + end + end + end + + describe 'PUT update' do + context 'with valid params' do + subject { put :update, params: {id: ascor_benchmark.id, ascor_benchmark: valid_attributes} } + + it 'updates the requested benchmark' do + expect { subject }.to change { ascor_benchmark.reload.country_id }.to(ascor_country.id) + end + + it 'redirects to the benchmark' do + expect(subject).to redirect_to(admin_ascor_benchmark_path(ascor_benchmark)) + end + end + + context 'with invalid params' do + subject { put :update, params: {id: ascor_benchmark.id, ascor_benchmark: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not update the requested benchmark' do + expect { subject }.not_to change(ascor_benchmark.reload, :country_id) + end + end + end + + describe 'DELETE destroy' do + subject { delete :destroy, params: {id: ascor_benchmark.id} } + + it 'destroys the requested benchmark' do + expect { subject }.to change(ASCOR::Benchmark, :count).by(-1) + end + + it 'redirects to the benchmarks list' do + expect(subject).to redirect_to(admin_ascor_benchmarks_path) + end + end +end diff --git a/spec/controllers/admin/ascor_countries_controller_spec.rb b/spec/controllers/admin/ascor_countries_controller_spec.rb new file mode 100644 index 000000000..4c9a4c7f0 --- /dev/null +++ b/spec/controllers/admin/ascor_countries_controller_spec.rb @@ -0,0 +1,94 @@ +require 'rails_helper' + +RSpec.describe Admin::ASCORCountriesController, type: :controller do + let(:admin) { create(:admin_user) } + let_it_be(:ascor_country) { create(:ascor_country) } + let(:valid_attributes) { attributes_for(:ascor_country).merge name: 'TEST', iso: 'TEST' } + let(:invalid_attributes) { valid_attributes.merge(name: nil) } + + before { sign_in admin } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_country.id} } + + it { is_expected.to be_successful } + end + + describe 'GET new' do + subject { get :new } + + it { is_expected.to be_successful } + end + + describe 'GET edit' do + subject { get :edit, params: {id: ascor_country.id} } + + it { is_expected.to be_successful } + end + + describe 'POST create' do + context 'with valid params' do + subject { post :create, params: {ascor_country: valid_attributes} } + + it 'creates a new ASCOR Country' do + expect { subject }.to change(ASCOR::Country, :count).by(1) + end + + it 'redirects to the created Country' do + expect(subject).to redirect_to(admin_ascor_country_path(ASCOR::Country.order(:created_at).last)) + end + end + + context 'with invalid params' do + subject { post :create, params: {ascor_country: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not create a ASCOR Country' do + expect { subject }.not_to change(ASCOR::Country, :count) + end + end + end + + describe 'PUT update' do + context 'with valid params' do + subject { put :update, params: {id: ascor_country.id, ascor_country: valid_attributes} } + + it 'updates the requested ASCOR Country' do + expect { subject }.to change { ascor_country.reload.name }.to(valid_attributes[:name]) + end + + it 'redirects to the ASCOR Country' do + expect(subject).to redirect_to(admin_ascor_country_path(ascor_country)) + end + end + + context 'with invalid params' do + subject { put :update, params: {id: ascor_country.id, ascor_country: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not update the ASCOR Country' do + expect { subject }.not_to change(ascor_country.reload, :name) + end + end + end + + describe 'DELETE destroy' do + subject { delete :destroy, params: {id: ascor_country.id} } + + it 'destroys the requested ASCOR Country' do + expect { subject }.to change(ASCOR::Country, :count).by(-1) + end + + it 'redirects to the ASCOR Countries list' do + expect(subject).to redirect_to(admin_ascor_countries_path) + end + end +end diff --git a/spec/controllers/admin/ascor_pathways_controller_spec.rb b/spec/controllers/admin/ascor_pathways_controller_spec.rb new file mode 100644 index 000000000..7b0ffaaf5 --- /dev/null +++ b/spec/controllers/admin/ascor_pathways_controller_spec.rb @@ -0,0 +1,95 @@ +require 'rails_helper' + +RSpec.describe Admin::ASCORPathwaysController, type: :controller do + let(:admin) { create(:admin_user) } + let_it_be(:ascor_country) { create :ascor_country, name: 'TEST', iso: 'TEST' } + let_it_be(:ascor_pathway) { create(:ascor_pathway) } + let(:valid_attributes) { attributes_for(:ascor_pathway).merge country_id: ascor_country.id } + let(:invalid_attributes) { valid_attributes.merge country_id: nil } + + before { sign_in admin } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_pathway.id} } + + it { is_expected.to be_successful } + end + + describe 'GET new' do + subject { get :new } + + it { is_expected.to be_successful } + end + + describe 'GET edit' do + subject { get :edit, params: {id: ascor_pathway.id} } + + it { is_expected.to be_successful } + end + + describe 'POST create' do + context 'with valid params' do + subject { post :create, params: {ascor_pathway: valid_attributes} } + + it 'creates a new ASCOR Pathway' do + expect { subject }.to change(ASCOR::Pathway, :count).by(1) + end + + it 'redirects to the created Pathway' do + expect(subject).to redirect_to(admin_ascor_pathway_path(ASCOR::Pathway.order(:created_at).last)) + end + end + + context 'with invalid params' do + subject { post :create, params: {ascor_pathway: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not create a ASCOR Pathway' do + expect { subject }.not_to change(ASCOR::Pathway, :count) + end + end + end + + describe 'PUT update' do + context 'with valid params' do + subject { put :update, params: {id: ascor_pathway.id, ascor_pathway: valid_attributes} } + + it 'updates the requested pathway' do + expect { subject }.to change { ascor_pathway.reload.country_id }.to(ascor_country.id) + end + + it 'redirects to the pathway' do + expect(subject).to redirect_to(admin_ascor_pathway_path(ascor_pathway)) + end + end + + context 'with invalid params' do + subject { put :update, params: {id: ascor_pathway.id, ascor_pathway: invalid_attributes} } + + it { is_expected.to be_successful } + + it 'invalid_attributes does not update the ASCOR Pathway' do + expect { subject }.not_to change(ascor_pathway.reload, :country_id) + end + end + end + + describe 'DELETE destroy' do + subject { delete :destroy, params: {id: ascor_pathway.id} } + + it 'destroys the requested pathway' do + expect { subject }.to change(ASCOR::Pathway, :count).by(-1) + end + + it 'redirects to the pathways list' do + expect(subject).to redirect_to(admin_ascor_pathways_path) + end + end +end diff --git a/spec/controllers/tpi/ascor_controller_spec.rb b/spec/controllers/tpi/ascor_controller_spec.rb new file mode 100644 index 000000000..6b64d0c9b --- /dev/null +++ b/spec/controllers/tpi/ascor_controller_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +RSpec.describe TPI::ASCORController, type: :controller do + let_it_be(:ascor_country) { create :ascor_country } + let_it_be(:ascor_assessment_indicator) { create :ascor_assessment_indicator } + let_it_be(:ascor_assessment) { create :ascor_assessment, country: ascor_country } + let_it_be(:ascor_assessment_result) do + create :ascor_assessment_result, assessment: ascor_assessment, indicator: ascor_assessment_indicator + end + let_it_be(:ascor_benchmark) { create :ascor_benchmark, country: ascor_country } + let_it_be(:ascor_pathway) { create :ascor_pathway, country: ascor_country } + + describe 'GET index' do + subject { get :index } + + it { is_expected.to be_successful } + end + + describe 'GET show' do + subject { get :show, params: {id: ascor_country.slug} } + + it { is_expected.to be_successful } + end + + describe 'GET user download' do + subject { get :user_download } + + it { is_expected.to be_successful } + + it 'returns zip file' do + subject + expect(response.content_type).to eq('application/zip') + end + + describe 'zip file' do + it 'has proper content' do + subject + + entries_names = [] + entries_csv_json = {} + zip_io = StringIO.new(response.body) + Zip::File.open_buffer(zip_io) do |zipfile| + zipfile.each do |entry| + entries_names << entry.name + entries_csv_json[entry.name] = parse_csv_to_json(entry.get_input_stream.read) if entry.name.ends_with?('.csv') + entries_csv_json[entry.name] = parse_xlsx_to_json(entry.get_input_stream.read) if entry.name.ends_with?('.xlsx') + end + end + + expect(entries_names).to include('ASCOR_countries.xlsx') + expect(entries_names).to include('ASCOR_indicators.xlsx') + expect(entries_names).to include('ASCOR_assessments_results.xlsx') + expect(entries_names).to include('ASCOR_benchmarks.xlsx') + expect(entries_names).to include('ASCOR_assessments_results_trends_pathways.xlsx') + + expect(entries_csv_json['ASCOR_countries.xlsx']).to match_snapshot('tpi_ascor_download_zip_countries_csv') + expect(entries_csv_json['ASCOR_indicators.xlsx']).to match_snapshot('tpi_ascor_download_zip_indicators_csv') + expect(entries_csv_json['ASCOR_assessments_results.xlsx']) + .to match_snapshot('tpi_ascor_download_zip_assessment_indicators_csv') + expect(entries_csv_json['ASCOR_benchmarks.xlsx']).to match_snapshot('tpi_ascor_download_zip_benchmarks_csv') + expect(entries_csv_json['ASCOR_assessments_results_trends_pathways.xlsx']) + .to match_snapshot('tpi_ascor_download_zip_pathways_csv') + end + end + end +end diff --git a/spec/factories/ascor_assessment.rb b/spec/factories/ascor_assessment.rb new file mode 100644 index 000000000..6f32947b4 --- /dev/null +++ b/spec/factories/ascor_assessment.rb @@ -0,0 +1,8 @@ +FactoryBot.define do + factory :ascor_assessment, class: 'ASCOR::Assessment' do + country factory: :ascor_country + assessment_date { Date.new(2021, 9, 10) } + publication_date { Date.new(2022, 1) } + notes { 'Research notes' } + end +end diff --git a/spec/factories/ascor_assessment_indicators.rb b/spec/factories/ascor_assessment_indicators.rb new file mode 100644 index 000000000..82bfe890e --- /dev/null +++ b/spec/factories/ascor_assessment_indicators.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: ascor_assessment_indicators +# +# id :bigint not null, primary key +# indicator_type :string +# code :string +# text :text +# created_at :datetime not null +# updated_at :datetime not null +# units_or_response_type :string +# +FactoryBot.define do + factory :ascor_assessment_indicator, class: ASCOR::AssessmentIndicator do + indicator_type { 'area' } + text { 'Emissions Trends' } + code { 'EP.1' } + units_or_response_type { 'Yes/No/Partial' } + end +end diff --git a/spec/factories/ascor_assessment_result.rb b/spec/factories/ascor_assessment_result.rb new file mode 100644 index 000000000..8f09de08a --- /dev/null +++ b/spec/factories/ascor_assessment_result.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :ascor_assessment_result, class: 'ASCOR::AssessmentResult' do + assessment factory: :ascor_assessment + indicator factory: :ascor_assessment_indicator + answer { 'Yes' } + source { 'https://www.google.com' } + year { 2018 } + end +end diff --git a/spec/factories/ascor_benchmarks.rb b/spec/factories/ascor_benchmarks.rb new file mode 100644 index 000000000..8f1ffc163 --- /dev/null +++ b/spec/factories/ascor_benchmarks.rb @@ -0,0 +1,35 @@ +# == Schema Information +# +# Table name: ascor_benchmarks +# +# id :bigint not null, primary key +# country_id :bigint not null +# publication_date :date +# emissions_metric :string +# emissions_boundary :string +# units :string +# benchmark_type :string +# emissions :jsonb +# created_at :datetime not null +# updated_at :datetime not null +# +FactoryBot.define do + factory :ascor_benchmark, class: 'ASCOR::Benchmark' do + country factory: :ascor_country + publication_date { Date.new(2021, 9) } + emissions_metric { 'Absolute' } + emissions_boundary { 'Production - excluding LULUCF' } + units { 'MtCO2e' } + benchmark_type { 'National 1.5C benchmark' } + emissions do + { + 2013 => 121, + 2014 => 124, + 2015 => 125, + 2016 => 120, + 2017 => 125, + 2018 => 128 + } + end + end +end diff --git a/spec/factories/ascor_countries.rb b/spec/factories/ascor_countries.rb new file mode 100644 index 000000000..96744c416 --- /dev/null +++ b/spec/factories/ascor_countries.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: ascor_countries +# +# id :bigint not null, primary key +# name :string +# slug :string +# iso :string +# region :string +# wb_lending_group :string +# fiscal_monitor_category :string +# created_at :datetime not null +# updated_at :datetime not null +# type_of_party :string +# +FactoryBot.define do + factory :ascor_country, class: 'ASCOR::Country' do + name { 'United States' } + iso { 'USA' } + region { 'North America' } + wb_lending_group { 'High-income economies' } + fiscal_monitor_category { 'Advanced economies' } + type_of_party { 'Annex I' } + end +end diff --git a/spec/factories/ascor_pathways.rb b/spec/factories/ascor_pathways.rb new file mode 100644 index 000000000..b57ebf2b2 --- /dev/null +++ b/spec/factories/ascor_pathways.rb @@ -0,0 +1,54 @@ +# == Schema Information +# +# Table name: ascor_pathways +# +# id :bigint not null, primary key +# country_id :bigint not null +# emissions_metric :string +# emissions_boundary :string +# units :string +# assessment_date :date +# publication_date :date +# last_historical_year :integer +# trend_1_year :string +# trend_3_year :string +# trend_5_year :string +# emissions :jsonb +# created_at :datetime not null +# updated_at :datetime not null +# trend_source :string +# trend_year :integer +# recent_emission_level :float +# recent_emission_source :string +# recent_emission_year :integer +# +FactoryBot.define do + factory :ascor_pathway, class: 'ASCOR::Pathway' do + country factory: :ascor_country + assessment_date { Date.new(2021, 9, 10) } + publication_date { Date.new(2022, 1) } + last_historical_year { 2010 } + + emissions_metric { 'Absolute' } + emissions_boundary { 'Production - excluding LULUCF' } + units { 'MtCO2e' } + emissions do + { + 2013 => 121, + 2014 => 124, + 2015 => 125, + 2016 => 120, + 2017 => 125, + 2018 => 128 + } + end + trend_1_year { '0%' } + trend_3_year { '+6%' } + trend_5_year { '+8%' } + trend_source { 'https://zenodo.org/record/7727481' } + trend_year { 2018 } + recent_emission_level { 128 } + recent_emission_source { 'https://zenodo.org/record/7727481' } + recent_emission_year { 2018 } + end +end diff --git a/spec/fixtures/snapshots/tpi_ascor_download_zip_assessment_indicators_csv.json b/spec/fixtures/snapshots/tpi_ascor_download_zip_assessment_indicators_csv.json new file mode 100644 index 000000000..bc6a115dc --- /dev/null +++ b/spec/fixtures/snapshots/tpi_ascor_download_zip_assessment_indicators_csv.json @@ -0,0 +1,11 @@ +[ + { + "Id": "12", + "Assessment date": "10/09/2021", + "Publication date": "01/01/2022", + "Country Id": "13", + "Country": "United States", + "area EP.1": "Yes", + "Notes": "Research notes" + } +] \ No newline at end of file diff --git a/spec/fixtures/snapshots/tpi_ascor_download_zip_benchmarks_csv.json b/spec/fixtures/snapshots/tpi_ascor_download_zip_benchmarks_csv.json new file mode 100644 index 000000000..c9ec52c3f --- /dev/null +++ b/spec/fixtures/snapshots/tpi_ascor_download_zip_benchmarks_csv.json @@ -0,0 +1,17 @@ +[ + { + "Id": "4", + "Country": "United States", + "Publication date": "01/09/2021", + "Emissions metric": "Absolute", + "Emissions boundary": "Production - excluding LULUCF", + "Units": "MtCO2e", + "Benchmark type": "National 1.5C benchmark", + "2013": "121", + "2014": "124", + "2015": "125", + "2016": "120", + "2017": "125", + "2018": "128" + } +] \ No newline at end of file diff --git a/spec/fixtures/snapshots/tpi_ascor_download_zip_countries_csv.json b/spec/fixtures/snapshots/tpi_ascor_download_zip_countries_csv.json new file mode 100644 index 000000000..8b9649524 --- /dev/null +++ b/spec/fixtures/snapshots/tpi_ascor_download_zip_countries_csv.json @@ -0,0 +1,11 @@ +[ + { + "Id": "7", + "Name": "United States", + "Country ISO code": "USA", + "Region": "North America", + "World Bank lending group": "High-income economies", + "International Monetary Fund fiscal monitor category": "Advanced economies", + "Type of Party to the United Nations Framework Convention on Climate Change": "Annex I" + } +] \ No newline at end of file diff --git a/spec/fixtures/snapshots/tpi_ascor_download_zip_indicators_csv.json b/spec/fixtures/snapshots/tpi_ascor_download_zip_indicators_csv.json new file mode 100644 index 000000000..6dd9569da --- /dev/null +++ b/spec/fixtures/snapshots/tpi_ascor_download_zip_indicators_csv.json @@ -0,0 +1,9 @@ +[ + { + "Id": "1", + "Type": "area", + "Code": "EP.1", + "Text": "Emissions Trends", + "Units or response type": "Yes/No/Partial" + } +] \ No newline at end of file diff --git a/spec/fixtures/snapshots/tpi_ascor_download_zip_pathways_csv.json b/spec/fixtures/snapshots/tpi_ascor_download_zip_pathways_csv.json new file mode 100644 index 000000000..ce1168f39 --- /dev/null +++ b/spec/fixtures/snapshots/tpi_ascor_download_zip_pathways_csv.json @@ -0,0 +1,26 @@ +[ + { + "Id": "12", + "Country": "United States", + "Emissions metric": "Absolute", + "Emissions boundary": "Production - excluding LULUCF", + "Units": "MtCO2e", + "Assessment date": "10/09/2021", + "Publication date": "01/01/2022", + "Last historical year": "2010", + "metric EP1.a.i": "128.0", + "source metric EP1.a.i": "https://zenodo.org/record/7727481", + "year metric EP1.a.i": "2018", + "metric EP1.a.ii 1-year": "0%", + "metric EP1.a.ii 3-year": "+6%", + "metric EP1.a.ii 5-year": "+8%", + "source metric EP1.a.ii": "https://zenodo.org/record/7727481", + "year metric EP1.a.ii": "2018", + "2013": "121", + "2014": "124", + "2015": "125", + "2016": "120", + "2017": "125", + "2018": "128" + } +] \ No newline at end of file diff --git a/spec/models/ascor/assessment_indicator_spec.rb b/spec/models/ascor/assessment_indicator_spec.rb new file mode 100644 index 000000000..4d93e717d --- /dev/null +++ b/spec/models/ascor/assessment_indicator_spec.rb @@ -0,0 +1,38 @@ +# == Schema Information +# +# Table name: ascor_assessment_indicators +# +# id :bigint not null, primary key +# indicator_type :string +# code :string +# text :text +# created_at :datetime not null +# updated_at :datetime not null +# units_or_response_type :string +# +require 'rails_helper' + +RSpec.describe ASCOR::AssessmentIndicator, type: :model do + subject { build(:ascor_assessment_indicator) } + + it { is_expected.to be_valid } + + it 'should be invalid without indicator_type' do + subject.indicator_type = nil + expect(subject).to have(1).errors_on(:indicator_type) + end + + it 'should be invalid without code' do + subject.code = nil + expect(subject).to have(1).errors_on(:code) + end + + it 'should be invalid without text' do + subject.text = nil + expect(subject).to have(1).errors_on(:text) + end + + it 'should be invalid if indicator_type is not in INDICATOR_TYPES' do + expect { subject.indicator_type = 'TEST' }.to raise_error(ArgumentError) + end +end diff --git a/spec/models/ascor/assessment_result_spec.rb b/spec/models/ascor/assessment_result_spec.rb new file mode 100644 index 000000000..bd21d938d --- /dev/null +++ b/spec/models/ascor/assessment_result_spec.rb @@ -0,0 +1,37 @@ +# == Schema Information +# +# Table name: ascor_assessment_results +# +# id :bigint not null, primary key +# assessment_id :bigint not null +# indicator_id :bigint not null +# answer :string +# created_at :datetime not null +# updated_at :datetime not null +# source :string +# year :integer +# +require 'rails_helper' + +RSpec.describe ASCOR::AssessmentResult, type: :model do + subject { build(:ascor_assessment_result) } + + it { is_expected.to be_valid } + + it 'should be invalid without assessment' do + subject.assessment = nil + expect(subject).to have(1).errors_on(:assessment) + end + + it 'should be invalid without indicator' do + subject.indicator = nil + expect(subject).to have(1).errors_on(:indicator) + end + + it 'should be invalid when result already exists for assessment and indicator' do + result = create :ascor_assessment_result + subject.assessment = result.assessment + subject.indicator = result.indicator + expect(subject).to have(1).errors_on(:indicator_id) + end +end diff --git a/spec/models/ascor/assessment_spec.rb b/spec/models/ascor/assessment_spec.rb new file mode 100644 index 000000000..09f112975 --- /dev/null +++ b/spec/models/ascor/assessment_spec.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: ascor_assessments +# +# id :bigint not null, primary key +# country_id :bigint not null +# assessment_date :date +# publication_date :date +# created_at :datetime not null +# updated_at :datetime not null +# notes :text +# +require 'rails_helper' + +RSpec.describe ASCOR::Assessment, type: :model do + subject { build(:ascor_assessment) } + + it { is_expected.to be_valid } + + it 'should be invalid without assessment_date' do + subject.assessment_date = nil + expect(subject).to have(1).errors_on(:assessment_date) + end +end diff --git a/spec/models/ascor/benchmarks_spec.rb b/spec/models/ascor/benchmarks_spec.rb new file mode 100644 index 000000000..d402f7f9f --- /dev/null +++ b/spec/models/ascor/benchmarks_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.describe ASCOR::Benchmark, type: :model do + subject { build(:ascor_benchmark) } + + it { is_expected.to be_valid } + + it 'should be invalid without country' do + subject.country = nil + expect(subject).to have(1).errors_on(:country) + end + + it 'should be invalid without emissions_metric' do + subject.emissions_metric = nil + expect(subject).to have(1).errors_on(:emissions_metric) + end + + it 'should be invalid without emissions_boundary' do + subject.emissions_boundary = nil + expect(subject).to have(1).errors_on(:emissions_boundary) + end + + it 'should be invalid without units' do + subject.units = nil + expect(subject).to have(1).errors_on(:units) + end + + it 'should be invalid without benchmark_type' do + subject.benchmark_type = nil + expect(subject).to have(1).errors_on(:benchmark_type) + end + + it 'should be invalid if emissions_metric is not in METRICS' do + subject.emissions_metric = 'TEST' + expect(subject).to have(1).errors_on(:emissions_metric) + end + + it 'should be invalid if emissions_boundary is not in BOUNDARIES' do + subject.emissions_boundary = 'TEST' + expect(subject).to have(1).errors_on(:emissions_boundary) + end +end diff --git a/spec/models/ascor/countries_spec.rb b/spec/models/ascor/countries_spec.rb new file mode 100644 index 000000000..74ef9f1fb --- /dev/null +++ b/spec/models/ascor/countries_spec.rb @@ -0,0 +1,64 @@ +require 'rails_helper' + +RSpec.describe ASCOR::Country, type: :model do + subject { build(:ascor_country) } + + it { is_expected.to be_valid } + + it 'should be invalid without name' do + subject.name = nil + expect(subject).to have(1).errors_on(:name) + end + + it 'should be invalid without iso' do + subject.iso = nil + expect(subject).to have(1).errors_on(:iso) + end + + it 'should be invalid without region' do + subject.region = nil + expect(subject).to have(1).errors_on(:region) + end + + it 'should be invalid without wb_lending_group' do + subject.wb_lending_group = nil + expect(subject).to have(1).errors_on(:wb_lending_group) + end + + it 'should be invalid without fiscal_monitor_category' do + subject.fiscal_monitor_category = nil + expect(subject).to have(1).errors_on(:fiscal_monitor_category) + end + + it 'should be invalid if name is taken' do + create(:ascor_country, name: 'TEST') + subject.name = 'TEST' + expect(subject).to have(1).errors_on(:name) + end + + it 'should be invalid if iso is taken' do + create(:ascor_country, iso: 'TEST') + subject.iso = 'TEST' + expect(subject).to have(1).errors_on(:iso) + end + + it 'should be invalid if region is not in REGIONS' do + subject.region = 'TEST' + expect(subject).to have(1).errors_on(:region) + end + + it 'should be invalid if wb_lending_group is not in LENDING_GROUPS' do + subject.wb_lending_group = 'TEST' + expect(subject).to have(1).errors_on(:wb_lending_group) + end + + it 'should be invalid if fiscal_monitor_category is not in MONITOR_CATEGORIES' do + subject.fiscal_monitor_category = 'TEST' + expect(subject).to have(1).errors_on(:fiscal_monitor_category) + end + + it 'should be invalid if type_of_party is not in TYPES_OF_PARTY' do + subject.type_of_party = 'TEST' + expect(subject).to have(1).errors_on(:type_of_party) + end +end diff --git a/spec/models/ascor/pathways_spec.rb b/spec/models/ascor/pathways_spec.rb new file mode 100644 index 000000000..b1e4d753c --- /dev/null +++ b/spec/models/ascor/pathways_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.describe ASCOR::Pathway, type: :model do + subject { build(:ascor_pathway) } + + it { is_expected.to be_valid } + + it 'should be invalid without country' do + subject.country = nil + expect(subject).to have(1).errors_on(:country) + end + + it 'should be invalid without emissions_metric' do + subject.emissions_metric = nil + expect(subject).to have(1).errors_on(:emissions_metric) + end + + it 'should be invalid without emissions_boundary' do + subject.emissions_boundary = nil + expect(subject).to have(1).errors_on(:emissions_boundary) + end + + it 'should be invalid without units' do + subject.units = nil + expect(subject).to have(1).errors_on(:units) + end + + it 'should be invalid without assessment_date' do + subject.assessment_date = nil + expect(subject).to have(1).errors_on(:assessment_date) + end + + it 'should be invalid if emissions_metric is not in METRICS' do + subject.emissions_metric = 'TEST' + expect(subject).to have(1).errors_on(:emissions_metric) + end + + it 'should be invalid if emissions_boundary is not in BOUNDARIES' do + subject.emissions_boundary = 'TEST' + expect(subject).to have(1).errors_on(:emissions_boundary) + end +end diff --git a/spec/models/company_spec.rb b/spec/models/company_spec.rb index dc4846f37..8f45b81b9 100644 --- a/spec/models/company_spec.rb +++ b/spec/models/company_spec.rb @@ -104,4 +104,30 @@ end end end + + describe '#beta_mq_assessments?' do + context 'when company has beta MQ assessments' do + let(:beta_methodology_version) { MQ::Assessment::BETA_METHODOLOGIES.keys.first } + let(:company) do + create(:company, mq_assessments: [ + build(:mq_assessment, assessment_date: '2012-05-01'), + build(:mq_assessment, assessment_date: '2019-06-01', methodology_version: beta_methodology_version) + ]) + end + + it 'returns true' do + expect(company.beta_mq_assessments?).to be_truthy + end + end + + context 'when company does not have beta MQ assessments' do + let(:company) do + create(:company, mq_assessments: [build(:mq_assessment, assessment_date: '2012-05-01')]) + end + + it 'returns false' do + expect(company.beta_mq_assessments?).to be_falsey + end + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9a2ba2cd5..f37135d61 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -52,6 +52,7 @@ RSpec.configure do |config| config.request_snapshots_dir = 'spec/fixtures/snapshots' + config.request_snapshots_dynamic_attributes = ['Id', 'Country Id'] config.include FactoryBot::Syntax::Methods config.include Devise::Test::ControllerHelpers, type: :controller diff --git a/spec/services/api/ascor/bubble_chart_spec.rb b/spec/services/api/ascor/bubble_chart_spec.rb new file mode 100644 index 000000000..039a6adf1 --- /dev/null +++ b/spec/services/api/ascor/bubble_chart_spec.rb @@ -0,0 +1,105 @@ +require 'rails_helper' + +RSpec.describe Api::ASCOR::BubbleChart do + subject { described_class.new(assessment_date).call } + + before_all do + usa = create(:ascor_country, id: 1, name: 'USA', iso: 'USA') + japan = create(:ascor_country, id: 2, name: 'Japan', iso: 'JPN') + + _indicator_pillar_1 = create(:ascor_assessment_indicator, id: 1, code: 'EP', indicator_type: :pillar, + text: 'Emissions Performance') + _indicator_pillar_2 = create(:ascor_assessment_indicator, id: 2, code: 'CP', indicator_type: :pillar, + text: 'Climate Performance') + indicator_area_1 = create(:ascor_assessment_indicator, id: 3, code: 'EP.1', indicator_type: :area, + text: 'Emissions Performance 1') + indicator_area_2 = create(:ascor_assessment_indicator, id: 4, code: 'EP.2', indicator_type: :area, + text: 'Emissions Performance 2') + indicator_area_3 = create(:ascor_assessment_indicator, id: 5, code: 'CP.1', indicator_type: :area, + text: 'Climate Performance 1') + + create :ascor_assessment, country: usa, assessment_date: Date.new(2019, 1, 1), results: [ + build(:ascor_assessment_result, indicator: indicator_area_1, answer: 'Yes'), + build(:ascor_assessment_result, indicator: indicator_area_2, answer: 'No'), + build(:ascor_assessment_result, indicator: indicator_area_3, answer: 'Yes') + ] + create :ascor_assessment, country: usa, assessment_date: Date.new(2019, 2, 1), results: [ + build(:ascor_assessment_result, indicator: indicator_area_1, answer: 'Yes'), + build(:ascor_assessment_result, indicator: indicator_area_2, answer: 'No'), + build(:ascor_assessment_result, indicator: indicator_area_3, answer: 'No') + ] + create :ascor_assessment, country: japan, assessment_date: Date.new(2019, 2, 1), results: [ + build(:ascor_assessment_result, indicator: indicator_area_1, answer: 'No'), + build(:ascor_assessment_result, indicator: indicator_area_2, answer: 'Yes'), + build(:ascor_assessment_result, indicator: indicator_area_3, answer: 'No') + ] + + create :ascor_pathway, country: usa, assessment_date: Date.new(2019, 1, 1), emissions_metric: 'Intensity per capita', + emissions_boundary: 'Production - excluding LULUCF', recent_emission_level: 100 + create :ascor_pathway, country: usa, assessment_date: Date.new(2019, 2, 1), emissions_metric: 'Intensity per capita', + emissions_boundary: 'Production - excluding LULUCF', recent_emission_level: 200 + create :ascor_pathway, country: japan, assessment_date: Date.new(2019, 2, 1), emissions_metric: 'Intensity per capita', + emissions_boundary: 'Production - excluding LULUCF', recent_emission_level: 300 + end + + let(:assessment_date) { Date.new(2019, 2, 1) } + + it 'returns the correct data' do + expect(subject).to match_array( + [{ + pillar: 'Emissions Performance', + area: 'Emissions Performance 1', + result: 'Yes', + country_id: 1, + country_name: 'USA', + country_path: '/ascor/usa', + market_cap_group: :small + }, + { + pillar: 'Emissions Performance', + area: 'Emissions Performance 1', + result: 'No', + country_id: 2, + country_name: 'Japan', + country_path: '/ascor/japan', + market_cap_group: :medium + }, + { + pillar: 'Emissions Performance', + area: 'Emissions Performance 2', + result: 'No', + country_id: 1, + country_name: 'USA', + country_path: '/ascor/usa', + market_cap_group: :small + }, + { + pillar: 'Emissions Performance', + area: 'Emissions Performance 2', + result: 'Yes', + country_id: 2, + country_name: 'Japan', + country_path: '/ascor/japan', + market_cap_group: :medium + }, + { + pillar: 'Climate Performance', + area: 'Climate Performance 1', + result: 'No', + country_id: 1, + country_name: 'USA', + country_path: '/ascor/usa', + market_cap_group: :small + }, + { + pillar: 'Climate Performance', + area: 'Climate Performance 1', + result: 'No', + country_id: 2, + country_name: 'Japan', + country_path: '/ascor/japan', + market_cap_group: :medium + }] + ) + end +end diff --git a/spec/services/api/ascor/emissions_chart_spec.rb b/spec/services/api/ascor/emissions_chart_spec.rb new file mode 100644 index 000000000..252cd01cd --- /dev/null +++ b/spec/services/api/ascor/emissions_chart_spec.rb @@ -0,0 +1,99 @@ +require 'rails_helper' + +RSpec.describe Api::ASCOR::EmissionsChart do + subject { described_class.new(assessment_date, emissions_metric, emissions_boundary, country_ids).call } + + before_all do + @usa = create(:ascor_country, id: 1, name: 'USA', iso: 'USA') + @czechia = create(:ascor_country, id: 2, name: 'Czechia', iso: 'CZE') + + create :ascor_pathway, + country: @usa, + assessment_date: Date.new(2019, 1, 1), + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Consumption - excluding LULUCF', + emissions: {2013 => 100, 2014 => 100, 2015 => 100} + create :ascor_pathway, + country: @usa, + assessment_date: Date.new(2019, 2, 1), + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Consumption - excluding LULUCF', + emissions: {2013 => 110, 2014 => 110, 2015 => 110} + create :ascor_pathway, + country: @czechia, + assessment_date: Date.new(2019, 2, 1), + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Consumption - excluding LULUCF', + emissions: {2013 => 120, 2014 => 120, 2015 => 120} + create :ascor_pathway, + country: @usa, + assessment_date: Date.new(2019, 2, 1), + emissions_metric: 'Absolute', + emissions_boundary: 'Production - excluding LULUCF', + emissions: {2013 => 130, 2014 => 130, 2015 => 130} + create :ascor_pathway, + country: @czechia, + assessment_date: Date.new(2019, 2, 1), + emissions_metric: 'Absolute', + emissions_boundary: 'Production - excluding LULUCF', + emissions: {2013 => 140, 2014 => 140, 2015 => 140} + end + + let(:assessment_date) { Date.new(2019, 2, 1) } + let(:emissions_metric) { 'Intensity per capita' } + let(:emissions_boundary) { 'Consumption - excluding LULUCF' } + let(:country_ids) { [@usa.id, @czechia.id] } + + it 'returns expected result' do + expect(subject).to eq( + data: { + @usa.id => { + emissions: {'2013' => 110, '2014' => 110, '2015' => 110}, + last_historical_year: 2010 + }, + @czechia.id => { + emissions: {'2013' => 120, '2014' => 120, '2015' => 120}, + last_historical_year: 2010 + } + }, + metadata: {unit: 'MtCO2e'} + ) + end + + context 'when country_ids are nil' do + let(:country_ids) { nil } + + it 'returns expected result' do + expect(subject).to eq( + data: { + @usa.id => { + emissions: {'2013' => 110, '2014' => 110, '2015' => 110}, + last_historical_year: 2010 + } + }, + metadata: {unit: 'MtCO2e'} + ) + end + end + + context 'when emissions_metric and emissions_boundary is nil' do + let(:emissions_metric) { nil } + let(:emissions_boundary) { nil } + + it 'returns expected result' do + expect(subject).to eq( + data: { + @usa.id => { + emissions: {'2013' => 130, '2014' => 130, '2015' => 130}, + last_historical_year: 2010 + }, + @czechia.id => { + emissions: {'2013' => 140, '2014' => 140, '2015' => 140}, + last_historical_year: 2010 + } + }, + metadata: {unit: 'MtCO2e'} + ) + end + end +end diff --git a/spec/services/api/ascor/recent_emissions_spec.rb b/spec/services/api/ascor/recent_emissions_spec.rb new file mode 100644 index 000000000..26ded6837 --- /dev/null +++ b/spec/services/api/ascor/recent_emissions_spec.rb @@ -0,0 +1,82 @@ +require 'rails_helper' + +RSpec.describe Api::ASCOR::RecentEmissions do + subject { described_class.new(@assessment_date, @country).call } + + before_all do + @country = create(:ascor_country, id: 1, name: 'USA', iso: 'USA') + @assessment_date = Date.new(2019, 1, 1) + + create :ascor_pathway, + country: @country, + assessment_date: Date.new(2019, 1, 1), + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Consumption - excluding LULUCF', + units: 'MtCO2e', + trend_1_year: '+ 6%', + trend_3_year: '+ 8%', + trend_5_year: '+ 9%', + trend_source: 'source', + trend_year: 2019, + recent_emission_level: 100, + recent_emission_source: 'source', + recent_emission_year: 2019 + create :ascor_pathway, + country: @country, + assessment_date: Date.new(2019, 1, 1), + emissions_metric: 'Absolute', + emissions_boundary: 'Production - only LULUCF', + units: 'MtCO2e', + trend_1_year: '- 6%', + trend_3_year: '+ 6%', + trend_5_year: '+ 8%', + trend_source: 'source 2', + trend_year: 2020, + recent_emission_level: 200, + recent_emission_source: 'source 2', + recent_emission_year: 2020 + create :ascor_pathway, country: create(:ascor_country, id: 2, name: 'Czechia', iso: 'CZE') + create :ascor_pathway, country: @country, assessment_date: Date.new(2019, 2, 1) + end + + it 'returns expected result' do + expect(subject).to match_array( + [ + { + value: 100, + source: 'source', + year: 2019, + unit: 'MtCO2e', + emissions_metric: 'Intensity per capita', + emissions_boundary: 'Consumption - excluding LULUCF', + trend: { + source: 'source', + year: 2019, + values: [ + {filter: '1 year trend', value: '+ 6%'}, + {filter: '3 years trend', value: '+ 8%'}, + {filter: '5 years trend', value: '+ 9%'} + ] + } + }, + { + value: 200, + source: 'source 2', + year: 2020, + unit: 'MtCO2e', + emissions_metric: 'Absolute', + emissions_boundary: 'Production - only LULUCF', + trend: { + source: 'source 2', + year: 2020, + values: [ + {filter: '1 year trend', value: '- 6%'}, + {filter: '3 years trend', value: '+ 6%'}, + {filter: '5 years trend', value: '+ 8%'} + ] + } + } + ] + ) + end +end diff --git a/spec/support/capybara_helpers.rb b/spec/support/capybara_helpers.rb index 6b5682e0c..35f43f16b 100644 --- a/spec/support/capybara_helpers.rb +++ b/spec/support/capybara_helpers.rb @@ -21,6 +21,15 @@ def within_banking_area(header_text) end end + def within_ascor_pillar(header_text) + within( + :xpath, + "(.//div[#{contains_class('country-assessment__pillar')}]//h2[contains(., '#{header_text}')]/../../..)[1]" + ) do + yield if block_given? + end + end + def with_mq_beta_scores within '.mq-beta-scores' do click_on 'BETA' @@ -33,4 +42,10 @@ def with_mq_beta_scores click_on 'Current' end end + + def for_mobile_screen + current_window.resize_to(375, 812) + yield + current_window.resize_to(1400, 800) + end end diff --git a/spec/support/fixtures/files/ascor_assessment_indicators.csv b/spec/support/fixtures/files/ascor_assessment_indicators.csv new file mode 100644 index 000000000..1d1ae19d6 --- /dev/null +++ b/spec/support/fixtures/files/ascor_assessment_indicators.csv @@ -0,0 +1,8 @@ +Id,Type,Code,Text,Units or response type +,pillar,EP,Emissions Pathways, +,area,EP.1,Emissions Trends,Yes/No/Partial +,indicator,EP.1.a,Have the country’s emissions decreased in recent years?,Yes/No +,metric,EP.1.a.i,What is the country's most recent emissions level?,MtCO2e +,metric,EP.1.a.ii,What is the country's most recent emissions trend?,% +,indicator,EP.1.b,Is the most recent 3-year trend aligned with meeting the country's 1.5°C benchmark?,Yes/No +,indicator,EP.1.c,Is the most recent 3-year trend aligned with meeting the country’s 1.5°C fair share?,Yes/No \ No newline at end of file diff --git a/spec/support/fixtures/files/ascor_assessments.csv b/spec/support/fixtures/files/ascor_assessments.csv new file mode 100644 index 000000000..7824ee3ce --- /dev/null +++ b/spec/support/fixtures/files/ascor_assessments.csv @@ -0,0 +1,3 @@ +Id,Assessment Date,Publication Date,Country Id,Country,area EP.1,indicator EP.1.a,indicator EP.1.b,indicator EP.1.c,source indicator EP.1.a,source metric EP.1.a.i,source metric EP.1.a.ii,source indicator EP.1.b,source indicator EP.1.c,year metric EP.1.a.i,year metric EP.1.a.ii +,10/30/23,2023-12,,United States,No,No,No,No,https://zenodo.org/record/7727475,,,https://1p5ndc-pathways.climateanalytics.org/,,, +,10/30/23,2023-12,,Japan,Partial,Yes,No,No,https://zenodo.org/record/7727476,,,https://1p5ndc-pathways.climateanalytics.org/,,2010, \ No newline at end of file diff --git a/spec/support/fixtures/files/ascor_benchmarks.csv b/spec/support/fixtures/files/ascor_benchmarks.csv new file mode 100644 index 000000000..73a2fe421 --- /dev/null +++ b/spec/support/fixtures/files/ascor_benchmarks.csv @@ -0,0 +1,3 @@ +Id,Country,Publication date,Emissions metric,Emissions boundary,Units,Benchmark type,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030 +,United States,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark," 6,220 "," 5,845 "," 5,470 "," 5,095 "," 4,720 "," 4,345 "," 3,970 "," 3,595 "," 3,219 "," 2,844 " +,Japan,2023-12,Absolute,Production - excluding LULUCF,MtCO2e,National 1.5C benchmark," 6,220 "," 5,845 "," 5,470 "," 5,095 "," 4,720 "," 4,345 "," 3,970 "," 3,595 "," 3,219 "," 2,844 " \ No newline at end of file diff --git a/spec/support/fixtures/files/ascor_countries.csv b/spec/support/fixtures/files/ascor_countries.csv new file mode 100644 index 000000000..f156aa1b8 --- /dev/null +++ b/spec/support/fixtures/files/ascor_countries.csv @@ -0,0 +1,3 @@ +Id,Name,Country ISO code,Region,World Bank lending group,International Monetary Fund fiscal monitor category,Type of Party to the United Nations Framework Convention on Climate Change +,United States,USA,North America,High-income economies,Advanced economies,Annex I +,Japan,JPN,Asia,High-income economies,Advanced economies,Annex I \ No newline at end of file diff --git a/spec/support/fixtures/files/ascor_pathways.csv b/spec/support/fixtures/files/ascor_pathways.csv new file mode 100644 index 000000000..dd0643c8b --- /dev/null +++ b/spec/support/fixtures/files/ascor_pathways.csv @@ -0,0 +1,3 @@ +Id,Country,Emissions metric,Emissions boundary,Units,Assessment date,Publication date,Last historical year,metric EP1.a.i,source metric EP1.a.i,year metric EP1.a.i,metric EP1.a.ii 1-year,metric EP1.a.ii 3-year,metric EP1.a.ii 5-year,source metric EP1.a.ii,year metric EP1.a.ii,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030 +,United States,Absolute,Production - excluding LULUCF,MtCO2e,10/30/23,2023-12,2021,- 792.3 ,https://zenodo.org/record/7727475,2021,3.9%,-2.3%,-0.9%,https://zenodo.org/record/7727475,2021,NA,"7,380.0","7,480.0","7,260.0","6,800.0","7,020.0","6,860.0","6,620.0","6,800.0","6,860.0","6,700.0","6,550.0","6,510.0","6,700.0","6,580.0","5,990.0","6,220.0","5,981.5","5,743.0","5,504.5","5,266.0","5,027.5","4,789.0","4,550.4","4,311.9","4,073.4" +,Japan,Intensity per GDP-PPP,Consumption - excluding LULUCF,tCO2/Million I US$,10/30/23,2023-12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/spec/system/public/tpi/ascor_spec.rb b/spec/system/public/tpi/ascor_spec.rb new file mode 100644 index 000000000..67777e1e6 --- /dev/null +++ b/spec/system/public/tpi/ascor_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +describe 'ASCOR', type: 'system', site: 'tpi' do + describe 'all countries page' do + before do + visit '/ascor' + end + + it 'loads the page' do + expect(page).to have_text('All countries') + end + + it 'loads the mobile version of bubble chart ' do + for_mobile_screen do + within all('.bubble-chart__container')[0] do # mobile version + expect(page).to have_text('Emissions Pathways') + expect(page).to have_text('Climate Policies') + expect(page).to have_text('Climate Finance') + end + end + end + + it 'loads desktop version of bubble chart' do + within all('.bubble-chart__container')[1] do # desktop version + expect(page).to have_text('1. Emissions Pathways') + expect(page).to have_text('2. Climate Policies') + expect(page).to have_text('3. Climate Finance') + + expect(page).to have_text('EP 1. Emissions Trends') + expect(page).to have_text('CP 1. Climate Legislation') + expect(page).to have_text('CF 1. International Climate Finance') + end + end + end + + describe 'single country page' do + before do + visit '/ascor/japan' + end + + it 'shows assessment results' do + areas = ASCOR::AssessmentIndicator.area.order(:id) + ASCOR::AssessmentIndicator.pillar.order(:id).each do |pillar| + within_ascor_pillar pillar.text do + areas.select { |a| a.code.include? pillar.code }.each do |area| + expect(page).to have_text(area.text) + end + end + end + end + + it 'generates EP.1.a.i and EP.1.a.ii metrics' do + find("label[for='ascor_assessment_indicator_#{ASCOR::AssessmentIndicator.find_by(code: 'EP.1').id}']").click + expect(page).to have_text("i. What is the country's most recent emissions level?") + expect(page).to have_text("ii. What is the country's most recent emissions trend?") + end + end +end