diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 3e3a59d..127e4d5 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -22,4 +22,5 @@ @use "application_settings"; @use "main"; @use "header"; - @use "nav"; \ No newline at end of file + @use "nav"; + @use "organisation"; \ No newline at end of file diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index b70cad6..01357e5 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -81,6 +81,10 @@ a.link:hover { border: 3px solid black; } +.information-table tr :is(td, th):first-child { + width: 35%; +} + .type_doc_header#tabs { width: 75%; margin: 0 auto; diff --git a/app/helpers/organisations_helper.rb b/app/helpers/organisations_helper.rb index 1b84554..d07afde 100644 --- a/app/helpers/organisations_helper.rb +++ b/app/helpers/organisations_helper.rb @@ -1,2 +1,117 @@ module OrganisationsHelper + # Функции для раскраски + def group_key(code) + return nil if code.nil? || code.size < 2 + prefix = code[0, 2] + case prefix + when '21', '22', '23', '24' + '21-24' + else + prefix + end + end + + def keep_code?(code, type_data: 'min_max') + return false if code.nil? || code.size < 2 + prefix = code[0, 2] + return false unless prefix.match?(/\A\d{2}\z/) + if type_data == 'min_max' + return false if %w[16 17].include?(prefix) + return true if %w[21 22 23 24 25 29].include?(prefix) + return !code.end_with?('00') + elsif type_data == 'comparsion' + return code.end_with?('00') + end + end + + def parse_number(value) + return 0 if value.empty? || ['-', '(-)', 'X'].include?(value) + negative = value.start_with?('(') && value.end_with?(')') + value = value.tr('()', '') if negative + value = value.delete(' ') + return 0 unless value.match?(/\A\d+\z/) + number = value.to_i + negative ? -number : number + end + + def prepare_data(data, type_data: 'min_max') + return {} if data.nil? + filtered_data = data.reject do |(_, _), values| + values.all? { |value| ['-', '(-)', 'X'].include?(value) } + end + if type_data == 'min_max' + prepared_data = filtered_data.each_with_object({}) do |((_, code), values), result| + next if code.nil? || !keep_code?(code) + group = group_key(code) + result[group] ||= Hash.new { |h, k| h[k] = [] } + values.each_with_index do |value, index| + result[group][index + 1] << parse_number(value) + end + end + min_max_data = prepared_data.transform_values do |columns| + columns.transform_values do |values| + sorted = values.sort + size = sorted.size + p50 = size.odd? ? sorted[size / 2] : (sorted[size / 2 - 1] + sorted[size / 2]) / 2.0 + { min: sorted.first, p50: p50, max: sorted.last } + end + end + elsif type_data == 'comparsion' + prepared_data = filtered_data.each_with_object({}) do |((_, code), values), result| + next if code.nil? || !keep_code?(code, type_data: 'comparsion') + result[code] ||= [] + (0...values.size - 1).each do |index| + if parse_number(values[index]) == parse_number(values[index + 1]) + result[code] << nil + elsif + result[code] << (parse_number(values[index]) > parse_number(values[index + 1])) + end + end + result[code] << nil + end + end + end + + def get_color_for_cell(value, min:, median:, max:) + value = parse_number(value) + return rgb_to_hex(WHITE) if value.nil? || min.nil? || max.nil? || median.nil? + return rgb_to_hex(GREEN) if min == max + + if value <= median + ratio = median == min ? 0.0 : (value - min).to_f / (median - min) + rgb = interpolate_color(RED, YELLOW, clamp(ratio)) + else + ratio = max == median ? 0.0 : (value - median).to_f / (max - median) + rgb = interpolate_color(YELLOW, GREEN, clamp(ratio)) + end + + rgb_to_hex(rgb) + end + + def get_arrow_for_cell(value, condition) + return content_tag(:td, value) if condition.nil? + css_class = condition ? 'up': 'down' + content_tag(:td, value, class: css_class) + end + + + private + + def interpolate_color(from, to, ratio) + from.zip(to).map { |a, b| (a + (b - a) * ratio).round } + end + + def rgb_to_hex(rgb) + format("#%02X%02X%02X", *rgb) + end + + def clamp(value) + [[value, 0.0].max, 1.0].min + end + + RED = [248, 105, 107].freeze + YELLOW = [255, 235, 132].freeze + GREEN = [99, 190, 123].freeze + WHITE = [255, 255, 255].freeze + end diff --git a/app/views/organisations/show.html.slim b/app/views/organisations/show.html.slim index 7a0fd0c..dd06b90 100644 --- a/app/views/organisations/show.html.slim +++ b/app/views/organisations/show.html.slim @@ -5,6 +5,8 @@ - unless @info_about_organisation.any? = 'У данной организации пока не загружена отчетность' - else + - min_max_data = prepare_data(@current_doc_info1) + - comparsion_data = prepare_data(@current_doc_info1, type_data: 'comparsion') .informations-data h2 = "Сведения об организации" @@ -27,10 +29,18 @@ tr td = key[0] td = key[1] - - @current_doc_info1[key].each do |value| - td = value - + - @current_doc_info1[key].each_with_index do |value, index| + - key_group = key[1].nil? ? nil : group_key(key[1]) + - if min_max_data.has_key?(key_group) && keep_code?(key[1]) + - min_value = min_max_data[key_group][index + 1][:min] + - max_value = min_max_data[key_group][index + 1][:max] + - p50 = min_max_data[key_group][index + 1][:p50] + - color = get_color_for_cell(value, min: min_value, median: p50, max: max_value) + td style="background-color: #{color}" = value + - elsif comparsion_data.has_key?(key[1]) && keep_code?(key[1], type_data: 'comparsion') + - condition = comparsion_data[key[1]][index] + = get_arrow_for_cell(value, condition) + - else + td = value - else - h3 - = "Нет данных" - \ No newline at end of file + h3 = "Нет данных" \ No newline at end of file