app/models/chart_data_values.rb
class ChartDataValues
attr_reader :anaylsis_type, :title, :subtitle, :chart1_type, :chart1_subtype,
:y_axis_label, :x_axis_label, :x_axis_categories, :x_max_value, :x_min_value,
:advice_header, :advice_footer, :y2_axis_label, :y2_point_format, :y2_max, :x_axis_ranges, :annotations,
:transformations, :allowed_operations, :drilldown_available, :parent_timescale_description,
:uses_time_of_day, :y1_axis_choices, :explore_message, :pinch_and_zoom_message, :click_and_drag_message
X_AXIS_CATEGORIES = %w(S M T W T F S).freeze
BENCHMARK_LABELS = [
I18n.t('analytics.series_data_manager.series_name.benchmark_school'), I18n.t('analytics.series_data_manager.series_name.exemplar_school')
].freeze
def initialize(chart, chart_type, transformations: [], allowed_operations: {}, drilldown_available: false, parent_timescale_description: nil, y1_axis_choices: [])
if chart
@chart_type = chart_type
@chart = chart
@title = chart[:title]
@subtitle = chart[:subtitle]
@chart1_type = chart[:chart1_type]
@chart1_subtype = chart[:chart1_subtype]
@x_axis_categories = translate_categories_for(chart[:x_axis])
@x_axis_ranges = chart[:x_axis_ranges]
@x_max_value = chart[:x_max_value]
@x_min_value = chart[:x_min_value]
@x_axis_label = translated_series_item_for(chart[:x_axis_label]) if chart[:x_axis_label]
@y_axis_label = format_y_axis_label_for(chart[:y_axis_label])
@configuration = chart[:configuration]
@advice_header = chart[:advice_header]
@advice_footer = chart[:advice_footer]
@x_data = translate_data_keys_for(chart[:x_data])
@y2_data = translate_data_keys_for(chart[:y2_data])
@y2_chart_type = chart[:y2_chart_type]
@annotations = []
@y2_axis_label = '' # Set later
@transformations = transformations
@allowed_operations = allowed_operations
@drilldown_available = drilldown_available
@parent_timescale_description = parent_timescale_description
@uses_time_of_day = false
@y1_axis_choices = y1_axis_choices
@explore_message = I18n.t('chart_data_values.explore_message')
@pinch_and_zoom_message = I18n.t('chart_data_values.pinch_and_zoom_message')
@click_and_drag_message = I18n.t('chart_data_values.click_and_drag_message')
else
@title = I18n.t('chart_data_values.not_enough_data_message', chart_type: chart_type.to_s.capitalize)
end
@used_name_colours = []
end
def translate_categories_for(categories)
return categories unless categories.is_a? Array
return categories if @chart1_type == :scatter
categories.map { |category_label| translated_series_item_for(category_label) }
end
def translate_data_keys_for(data)
return unless data.present?
data.transform_keys { |series_item| translated_series_item_for(series_item) }
end
def format_y_axis_label_for(y_axis_label)
if y_axis_label == 'kg CO2'
'kg<br>CO2'
else
y_axis_label
end
end
def process
return self if @chart.nil?
@x_data_hash = reverse_x_data_if_required
@series_data = []
@annotations = annotations_configuration
if @chart1_type == :column || @chart1_type == :bar
if @chart_type.match?(/^public_displays/) || @chart_type.match?(/^calendar_picker/) && @chart[:configuration][:series_breakdown] != :meter
usage_column
else
column_or_bar
end
elsif @chart1_type == :scatter
scatter_and_trendline
elsif @chart1_type == :line
# TODO chart colours that show gas/electricity/storage should all be using usage_line.
if @chart_type.match?(/^targeting_and_tracking/) || @chart_type.match?(/^calendar_picker/) && @chart[:configuration][:series_breakdown] != :meter
usage_line
else
line
end
elsif @chart1_type == :pie
pie
end
self
end
def series_data
return @series_data unless @series_data.is_a? Array
# Temporary TOFIX TODO as analytics should not return negative values
@series_data.map do |series|
series[:data] = series[:data].map { |v| v.is_a?(Float) ? v.round(8) : v }
series
end
end
def colour_lookup
@colour_lookup ||= {
I18n.t("analytics.series_data_manager.series_name.#{Series::DegreeDays::DEGREEDAYS_I18N_KEY}") => Colours.chart_degree_days,
I18n.t("analytics.series_data_manager.series_name.#{Series::Temperature::TEMPERATURE_I18N_KEY}") => Colours.chart_temperature,
I18n.t("analytics.series_data_manager.series_name.#{Series::DayType::SCHOOLDAYCLOSED_I18N_KEY}") => Colours.chart_school_day_closed,
I18n.t("analytics.series_data_manager.series_name.#{Series::DayType::SCHOOLDAYOPEN_I18N_KEY}") => Colours.chart_school_day_open,
I18n.t("analytics.series_data_manager.series_name.#{Series::DayType::HOLIDAY_I18N_KEY}") => Colours.chart_holiday,
I18n.t("analytics.series_data_manager.series_name.#{Series::DayType::WEEKEND_I18N_KEY}") => Colours.chart_weekend,
I18n.t("analytics.series_data_manager.series_name.#{Series::HeatingNonHeating::HEATINGDAY_I18N_KEY}") => Colours.chart_heating_day,
I18n.t("analytics.series_data_manager.series_name.#{Series::HeatingNonHeating::NONHEATINGDAY_I18N_KEY}") => Colours.chart_non_heating_day,
I18n.t("analytics.series_data_manager.series_name.#{Series::HotWater::USEFULHOTWATERUSAGE_I18N_KEY}") => Colours.chart_useful_hot_water_usage,
I18n.t("analytics.series_data_manager.series_name.#{Series::HotWater::WASTEDHOTWATERUSAGE_I18N_KEY}") => Colours.chart_wasted_hot_water_usage,
I18n.t("analytics.series_data_manager.series_name.#{Series::MultipleFuels::SOLARPV_I18N_KEY}") => Colours.chart_solar_pv,
I18n.t('analytics.series_data_manager.series_name.electricity') => Colours.chart_electric,
I18n.t('analytics.series_data_manager.series_name.gas') => Colours.chart_gas,
I18n.t('analytics.series_data_manager.series_name.storage_heaters') => Colours.chart_storage_heater,
'£' => Colours.chart_gbp,
I18n.t("analytics.series_data_manager.series_name.#{SolarPVPanels::SOLAR_PV_ONSITE_ELECTRIC_CONSUMPTION_METER_NAME_I18N_KEY}") => Colours.chart_electricity_consumed_from_solar_pv,
I18n.t("analytics.series_data_manager.series_name.#{SolarPVPanels::ELECTRIC_CONSUMED_FROM_MAINS_METER_NAME_I18N_KEY}") => Colours.chart_electric_dark,
I18n.t("analytics.series_data_manager.series_name.#{SolarPVPanels::SOLAR_PV_EXPORTED_ELECTRIC_METER_NAME_I18N_KEY}") => Colours.chart_gas_light_line,
I18n.t('analytics.series_data_manager.y2_solar_label') => Colours.chart_y2_solar_label,
I18n.t('analytics.series_data_manager.y2_rating') => Colours.chart_y2_rating
}
end
def work_out_best_colour(data_type)
from_hash = colour_lookup[data_type]
return from_hash unless from_hash.nil?
using_name = colour_lookup.detect do |key, colour|
data_type.to_s.downcase.include?(key.downcase) && !@used_name_colours.include?(colour)
end
unless using_name.nil?
@used_name_colours << using_name.second
using_name.second
end
end
def self.as_chart_json(output)
[
:title,
:subtitle,
:chart1_type,
:chart1_subtype,
:y_axis_label,
:x_axis_label,
:x_axis_categories,
:x_max_value,
:x_min_value,
:advice_header,
:advice_footer,
:y2_axis_label,
:y2_point_format,
:y2_max,
:series_data,
:annotations,
:allowed_operations,
:drilldown_available,
:transformations,
:parent_timescale_description,
:uses_time_of_day,
:y1_axis_choices,
:explore_message,
:pinch_and_zoom_message,
:click_and_drag_message,
:subtitle_start_date,
:subtitle_end_date
].inject({}) do |json, field|
json[field] = output.public_send(field)
json
end
end
def subtitle_start_date
return nil unless x_axis_ranges_present? && transformations_empty_or_only_move?
format_subtitle_date(@x_axis_ranges.first.first)
end
def subtitle_end_date
return nil unless x_axis_ranges_present? && transformations_empty_or_only_move?
format_subtitle_date(@x_axis_ranges.last.last)
end
def format_subtitle_date(date)
date.to_fs(:es_short)
end
def translate_bill_component_series(series_key_as_string)
I18n.t("advice_pages.tables.labels.bill_components.#{series_key_as_string}")
end
def translated_series_item_for(series_key_as_string)
series_key_as_string = series_key_as_string.to_s
return I18n.t('analytics.series_data_manager.series_name.baseload') if series_key_as_string.casecmp('baseload').zero?
return I18n.t('advice_pages.benchmarks.benchmark_school') if series_key_as_string == 'benchmark'
return I18n.t('advice_pages.benchmarks.exemplar_school') if series_key_as_string == 'exemplar'
return I18n.t('analytics.common.school_day') if series_key_as_string == 'school day'
return translate_bill_component_series(series_key_as_string) if I18n.t('advice_pages.tables.labels.bill_components').keys.map(&:to_s).include?(series_key_as_string)
i18n_key = series_translation_key_lookup[series_key_as_string]
return series_key_as_string unless i18n_key
I18n.t("analytics.series_data_manager.series_name.#{i18n_key}")
end
def series_translation_key_lookup
@series_translation_key_lookup ||= {
Series::DegreeDays::DEGREEDAYS => Series::DegreeDays::DEGREEDAYS_I18N_KEY,
Series::Temperature::TEMPERATURE => Series::Temperature::TEMPERATURE_I18N_KEY,
Series::DayType::SCHOOLDAYCLOSED => Series::DayType::SCHOOLDAYCLOSED_I18N_KEY,
Series::DayType::SCHOOLDAYOPEN => Series::DayType::SCHOOLDAYOPEN_I18N_KEY,
Series::DayType::HOLIDAY => Series::DayType::HOLIDAY_I18N_KEY,
Series::DayType::WEEKEND => Series::DayType::WEEKEND_I18N_KEY,
Series::DayType::STORAGE_HEATER_CHARGE => Series::DayType::STORAGE_HEATER_CHARGE_I18N_KEY,
Series::HotWater::USEFULHOTWATERUSAGE => Series::HotWater::USEFULHOTWATERUSAGE_I18N_KEY,
Series::HotWater::WASTEDHOTWATERUSAGE => Series::HotWater::WASTEDHOTWATERUSAGE_I18N_KEY,
Series::Irradiance::IRRADIANCE => Series::Irradiance::IRRADIANCE_I18N_KEY,
Series::GridCarbon::GRIDCARBON => Series::GridCarbon::GRIDCARBON_I18N_KEY,
Series::GasCarbon::GASCARBON => Series::GasCarbon::GASCARBON_I18N_KEY,
Series::HeatingNonHeating::HEATINGDAY => Series::HeatingNonHeating::HEATINGDAY_I18N_KEY,
Series::HeatingNonHeating::NONHEATINGDAY => Series::HeatingNonHeating::NONHEATINGDAY_I18N_KEY,
Series::HeatingNonHeating::HEATINGDAYWARMWEATHER => Series::HeatingNonHeating::HEATINGDAYWARMWEATHER_I18N_KEY,
Series::MultipleFuels::ELECTRICITY => Series::MultipleFuels::ELECTRICITY_I18N_KEY,
Series::MultipleFuels::GAS => Series::MultipleFuels::GAS_I18N_KEY,
Series::MultipleFuels::STORAGEHEATERS => Series::MultipleFuels::STORAGEHEATERS_I18N_KEY,
Series::MultipleFuels::SOLARPV => Series::MultipleFuels::SOLARPV_I18N_KEY,
Series::PredictedHeat::PREDICTEDHEAT => Series::PredictedHeat::PREDICTEDHEAT_I18N_KEY,
Series::TargetDegreeDays::TARGETDEGREEDAYS => Series::TargetDegreeDays::TARGETDEGREEDAYS_I18N_KEY,
Series::Cusum::CUSUM => Series::Cusum::CUSUM_I18N_KEY,
Series::Baseload::BASELOAD => Series::Baseload::BASELOAD_I18N_KEY,
Series::PeakKw::PEAK_KW => Series::PeakKw::PEAK_KW_I18N_KEY,
Series::HeatingDayType::SCHOOLDAYHEATING => Series::HeatingDayType::SCHOOLDAYHEATING_I18N_KEY,
Series::HeatingDayType::HOLIDAYHEATING => Series::HeatingDayType::HOLIDAYHEATING_I18N_KEY,
Series::HeatingDayType::WEEKENDHEATING => Series::HeatingDayType::WEEKENDHEATING_I18N_KEY,
Series::HeatingDayType::SCHOOLDAYHOTWATER => Series::HeatingDayType::SCHOOLDAYHOTWATER_I18N_KEY,
Series::HeatingDayType::HOLIDAYHOTWATER => Series::HeatingDayType::HOLIDAYHOTWATER_I18N_KEY,
Series::HeatingDayType::WEEKENDHOTWATER => Series::HeatingDayType::WEEKENDHOTWATER_I18N_KEY,
Series::HeatingDayType::BOILEROFF => Series::HeatingDayType::BOILEROFF_I18N_KEY,
Series::NoBreakdown::NONE => Series::NoBreakdown::NONE_I18N_KEY,
AggregatorBenchmarks::EXEMPLAR_SCHOOL_NAME => 'exemplar_school',
AggregatorBenchmarks::BENCHMARK_SCHOOL_NAME => 'benchmark_school',
OpenCloseTime.community => OpenCloseTime::COMMUNITY_I18N_KEY,
OpenCloseTime.community_baseload => OpenCloseTime::COMMUNITY_BASELOAD_I18N_KEY,
SolarPVPanels::SOLAR_PV_ONSITE_ELECTRIC_CONSUMPTION_METER_NAME => SolarPVPanels::SOLAR_PV_ONSITE_ELECTRIC_CONSUMPTION_METER_NAME_I18N_KEY,
SolarPVPanels::SOLAR_PV_EXPORTED_ELECTRIC_METER_NAME => SolarPVPanels::SOLAR_PV_EXPORTED_ELECTRIC_METER_NAME_I18N_KEY,
SolarPVPanels::ELECTRIC_CONSUMED_FROM_MAINS_METER_NAME => SolarPVPanels::ELECTRIC_CONSUMED_FROM_MAINS_METER_NAME_I18N_KEY
}
end
private
def start_date_from_label(full_label)
# Remove leading Energy:
date_string = tidy_label(full_label)
Date.parse(date_string)
rescue ArgumentError
nil
end
def format_teachers_label(full_label)
start_date = start_date_from_label(full_label)
return full_label unless start_date
end_date = start_date + 6.days
"#{I18n.l(start_date, format: '%a %d/%m/%Y')} - #{I18n.l(end_date, format: '%a %d/%m/%Y')}"
rescue ArgumentError
full_label
end
def usage_column
@series_data = @x_data_hash.each_with_index.map do |(data_type, data), index|
colour = teachers_chart_colour(index)
# get the start date
start_date = start_date_from_label(data_type)
# run map over the data to turn it into a hash of {y: d, day: formatted_date from index}
if start_date
data.map!.with_index {|v, i| { y: v, day: I18n.l(start_date.next_day(i), format: '%a %d/%m/%Y') } }
end
# add some useful cue to the json to indicate it should use an alternate formatter
# e.g. pointFormat: :day, :orderedPoint
{ name: format_teachers_label(data_type), color: colour, type: @chart1_type, data: data, index: index, day_format: start_date.present? }
end
end
def teachers_chart_colour(index)
if @chart_type.match?(/_gas_/)
index.zero? ? Colours.chart_gas_dark : Colours.chart_gas_light
elsif @chart_type.match?(/_storage_/)
index.zero? ? Colours.chart_storage_dark : Colours.chart_storage_light
else
index.zero? ? Colours.chart_electric_dark : Colours.chart_electric_light
end
end
def wrap_label_as_html(label)
'<span>' + label.split.join('<br />') + '</span>'
end
def column_or_bar
@series_data = @x_data_hash.each_with_index.map do |(data_type, data), index|
data_type = tidy_label(data_type)
colour = work_out_best_colour(data_type)
# ToDo is there a better way we can detect this reliably?
if data.detect { |record| record.is_a?(TimeOfDay) }
@uses_time_of_day = true
data = data.map { |record| record.present? ? convert_relative_time(record.relative_time) : nil }
end
if is_benchmark_chart?
colour_benchmark_bars(data_type, data)
end
{ name: data_type, color: colour, type: @chart1_type, data: data, index: index }
end
if @y2_data != nil && @y2_chart_type == :line
y2_data_title = @y2_data.keys[0]
@y2_axis_label, @y2_point_format, @y2_max = label_point_and_max_for(y2_data_title)
@y2_data.each do |data_type, data|
data_type = I18n.t('analytics.series_data_manager.y2_solar_label') if y2_is_solar?(data_type)
@series_data << { name: data_type, color: work_out_best_colour(data_type), type: 'line', data: data, yAxis: 1 }
end
end
end
def label_point_and_max_for(y2_data_title)
if y2_is_temperature?(y2_data_title)
['°C', '{point.y:.2f} °C',]
elsif y2_is_degree_days?(y2_data_title)
[
wrap_label_as_html(y2_data_title),
"{point.y:.2f} #{y2_data_title}",
]
elsif y2_is_carbon_intensity?(y2_data_title)
['kg/kWh', '{point.y:.2f} kg/kWh', 0.5]
elsif y2_is_carbon?(y2_data_title)
['kWh', '{point.y:.2f} kWh',]
elsif y2_is_solar?(y2_data_title)
[
I18n.t('analytics.series_data_manager.y2_solar_html'),
'{point.y:.2f} W/m2',
]
elsif y2_is_rating?(y2_data_title)
[I18n.t('analytics.series_data_manager.y2_rating'),]
end
end
def trendline?(data_type)
data_type.to_s.downcase.start_with?('trendline')
end
def scatter_and_trendline
@x_data_hash.each do |data_type, data|
@series_data << {
name: data_type,
color: work_out_best_colour(data_type),
data: scatter_and_trendline_series_data_for(data_type, data)
}
end
end
def scatter_and_trendline_series_data_for(data_type, data)
if trendline?(data_type)
reduced_trendline_series_data_for(data)
else
scatter_series_data_for(data)
end
end
def reduced_trendline_series_data_for(data)
# Trendline data needs to be reduced to maximum and minimum values only to reliably plot
# a non-breaking straight line between two points.
maximum_value = data.compact.max
minimum_value = data.compact.min
reduced_data = data.map { |value| [maximum_value, minimum_value].include?(value) ? value : nil }
@x_axis_categories.zip(reduced_data)
end
def scatter_series_data_for(data)
@x_axis_categories.zip(data)
end
def usage_line
colour_options = case @chart_type
when /_gas_/ then [Colours.chart_gas_dark, Colours.chart_gas_light]
when /_storage_/ then [Colours.chart_storage_dark, Colours.chart_storage_light]
else [Colours.chart_electric_dark, Colours.chart_electric_light]
end
line(colour_options: colour_options)
end
def line(colour_options: [Colours.chart_green, Colours.chart_light_orange])
@series_data = @x_data_hash.each_with_index.map do |(data_type, data), index|
data_type = tidy_label(data_type)
{ name: data_type, color: colour_options[index], type: @chart1_type, data: data }
end
if @y2_data != nil && @y2_chart_type == :line
@series_data = @x_data_hash.each_with_index.map do |(data_type, data), index|
data_type = tidy_and_keep_label(data_type)
{ name: data_type, color: colour_options[index], type: @chart1_type, data: data }
end
@y2_axis_label = @y2_data.keys[0]
@y2_axis_label = translated_series_item_for('Temperature') if @y2_axis_label.start_with?('Temp')
@y2_data.each do |data_type, data|
data_type = tidy_and_keep_label(data_type)
@series_data << { name: data_type, color: work_out_best_colour(data_type), type: 'line', data: data, yAxis: 1 }
end
else
@series_data = @x_data_hash.each_with_index.map do |(data_type, data), index|
data_type = tidy_label(data_type)
{ name: data_type, color: colour_options[index], type: @chart1_type, data: data }
end
end
end
def pie
data_points = @x_data_hash.map do |data_type, data|
{ name: data_type, color: work_out_best_colour(data_type), type: @chart1_type, y: data[0] }
end
@series_data = { name: @title, colorByPoint: true, data: data_points }
end
def reverse_x_data_if_required
if @chart.dig(:data, :configuration, :series_name_order) == :reverse
@x_data.reverse_each.to_h
else
@x_data
end
end
def convert_relative_time(relative_time, offset = 1.hour)
(relative_time + offset).to_i * 1000
end
def label_is_energy_plus?(label)
label.is_a?(String) && label.start_with?('Energy') && label.length > 6
end
def tidy_label(current_label)
if label_is_energy_plus?(current_label)
current_label = sort_out_dates_when_tidying_labels(current_label)
end
current_label
end
def tidy_and_keep_label(current_label)
label_bit = current_label.scan(/\d+|[A-Za-z]+/).shift
label_bit + ' ' + sort_out_dates_when_tidying_labels(current_label)
end
def sort_out_dates_when_tidying_labels(current_label)
date_to_and_from = current_label.scan(/\d+|[A-Za-z]+/).drop(1).each_slice(4).to_a
if date_to_and_from.size > 1 && date_to_and_from[0][3] != date_to_and_from[1][3]
date_to_and_from[0].delete_at(0)
date_to_and_from[1].delete_at(0)
end
date_to_and_from.map { |bit| bit.join(' ') }.join(' - ')
end
def annotations_configuration
case @chart_type
when :management_dashboard_group_by_week_electricity, :management_dashboard_group_by_week_gas, :electricity_co2_last_year_weekly_with_co2_intensity then :weekly
when :baseload_lastyear then :daily
end
end
def y2_is_temperature?(y2_data_title)
y2_data_title == translated_series_item_for(Series::Temperature::TEMPERATURE)
end
def y2_is_degree_days?(y2_data_title)
y2_data_title == translated_series_item_for(Series::DegreeDays::DEGREEDAYS)
end
def y2_is_carbon_intensity?(y2_data_title)
return true if y2_data_title == translated_series_item_for(Series::GridCarbon::GRIDCARBON)
return true if y2_data_title == translated_series_item_for(Series::GasCarbon::GASCARBON)
return true if y2_data_title.downcase.starts_with?('carbon intensity')
false
end
def y2_is_carbon?(y2_data_title)
y2_data_title.starts_with?('Carbon')
end
def y2_is_solar?(y2_data_title)
return true if y2_data_title == translated_series_item_for(Series::Irradiance::IRRADIANCE)
return true if y2_data_title.downcase.starts_with?('solar') # TODO match against series constants
false
end
def y2_is_rating?(y2_data_title)
y2_data_title.casecmp('rating').zero?
end
def x_axis_ranges_present?
@x_axis_ranges.present? && !@x_axis_ranges.empty?
end
def transformations_empty_or_only_move?
return true if @transformations.nil? || @transformations.empty?
return true if @transformations.length == 1 && transformation_type(@transformations[0]) == :move
false
end
def transformation_type(transformation)
transformation.first
end
def is_benchmark_chart?
@configuration.present? && @configuration[:inject].present? && @configuration[:inject] == :benchmark
end
def colour_benchmark_bars(data_type, data)
@x_axis_categories.each_with_index do |category, index|
if BENCHMARK_LABELS.include?(category)
# replace the scalar value with an object that
# holds the original y axis data and specifies a custom colour
data[index] = {
y: data[index], color: benchmark_colour(data_type, category)
}
end
end
end
# category = benchmark, exemplar
# data_type = Gas, Electricity
def benchmark_colour(data_type, category)
# this has multiple fuel types
if [:benchmark, :benchmark_one_year].include?(@chart_type)
return colours_for_multiple_fuel_type_benchmark(data_type, category)
end
if @chart_type.match?(/_gas_/)
if benchmark_school_category?(category)
Colours.chart_gas_middle
else
Colours.chart_gas_light
end
elsif @chart_type.match?(/_storage_/)
Colours.chart_storage_dark
elsif benchmark_school_category?(category)
Colours.chart_electric_middle
else
Colours.chart_electric_light
end
end
def colours_for_multiple_fuel_type_benchmark(data_type, category)
case data_type
when translated_series_item_for('Gas')
if benchmark_school_category?(category)
Colours.chart_gas_middle
else
Colours.chart_gas_light
end
when translated_series_item_for('Electricity')
if benchmark_school_category?(category)
Colours.chart_electric_middle
else
Colours.chart_electric_light
end
else
Colours.chart_storage_dark
end
end
def benchmark_school_category?(category)
category == I18n.t('analytics.series_data_manager.series_name.benchmark_school')
end
end