zold-io/wts.zold.io

View on GitHub
objects/graph.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# Copyright (c) 2018-2024 Zerocracy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'zold/log'
require 'SVG/Graph/Line'
require_relative 'wts'
require_relative 'user_error'

#
# Graph in SVG.
#
# See: https://github.com/lumean/svg-graph2/blob/master/lib/SVG/Graph/Graph.rb
#
class WTS::Graph
  # How many total X-steps on the graph
  STEPS = 12
  private_constant :STEPS

  def initialize(ticks, log: Zold::Log::NULL)
    @ticks = ticks
    @log = log
  end

  def svg(keys, div, digits, title: '')
    sets = {}
    min = Time.now
    max = Time.now + (STEPS * 24 * 60 * 60)
    keys.each do |k|
      @ticks.fetch(k).each do |t|
        sets[k] = [] if sets[k].nil?
        sets[k] << { x: t[:created], y: t[:value] / div }
        min = t[:created] if min > t[:created]
        max = t[:created] if max < t[:created]
      end
    end
    raise WTS::UserError, 'E221: There are no ticks, sorry' if sets.empty?
    step = (max - min) / STEPS
    raise WTS::UserError, 'E222: Step is too small, can\'t render, sorry' if step.zero?
    params = {
      width: 400, height: 200,
      show_x_guidelines: true, show_y_guidelines: true,
      show_x_labels: true, show_y_labels: false,
      x_label_font_size: 10,
      key: keys.count > 1,
      step_include_first_x_label: false,
      stagger_x_labels: true,
      number_format: "%.#{digits}f",
      fields: (0..STEPS - 1).map { |i| (min + (i * step)).strftime('%m/%d') }
    }
    unless title.empty?
      params[:y_title] = title.gsub(/[^a-zA-Z0-9 ]/, ' ')
      params[:show_y_title] = true
    end
    g = SVG::Graph::Line.new(params)
    sets.each do |k, v|
      data = Array.new(STEPS, nil)
      v.group_by { |p| ((p[:x] - min) / step).to_i }.each do |s, points|
        data[s] = points.empty? ? 0 : points.map { |p| p[:y] }.max
      end
      g.add_data(title: k, data: data)
    end
    g.burn
  end
end