savonarola/pulse_meter_visualizer

View on GitHub
lib/pulse_meter/visualize/widgets/timeline.rb

Summary

Maintainability
A
1 hr
Test Coverage
module PulseMeter
  module Visualize
    module Widgets
      class Error < StandardError; end
      class Timeline < PulseMeter::Visualize::Widget

        class NotATimelinedSensorInWidget < PulseMeter::Visualize::Error; end
        class DifferentSensorIntervalsInWidget < PulseMeter::Visualize::Error; end

        DEFAULT_TIMESPAN = 3600

        def initialize(opts)
          super
          opts[:timespan] ||= DEFAULT_TIMESPAN
        end

        def data(options = {})
          from, till = get_interval_borders(options)
          super().merge({
            values_title: values_label,
            series: series_data(from, till),
            timespan: timespan,
            interval: interval
          })
        end

        protected

        def get_interval_borders(options)
          from = if options[:start_time] && (options[:start_time] > 0)
            Time.at options[:start_time]
          else
            tspan = options[:timespan] || timespan
            Time.now - tspan
          end

          till = if options[:end_time] && (options[:end_time] > 0)
            Time.at options[:end_time]
          else
            Time.now
          end
          [from, till]
        end

        def series_data(from, till)
          ensure_equal_intervals!
          sensor_datas = sensors.map{ |s|
            s.timeline_data(from, till, show_last_point)
          }
          rows = []
          titles = []
          series_options = []
          datas = []
          sensor_datas.each do |sensor_data|
            sensor_data.each do |tl|
              titles << tl[:name]
              series_options << {color: tl[:color]}
              datas << tl[:data]
            end
          end
          unless datas.empty?
            first = datas.shift
            first.each_with_index do |tl_data, row_num|
              rows << datas.each_with_object([tl_data[:x], tl_data[:y]]) do |data_col, row|
                row << data_col[row_num][:y]
              end
            end
          end
          {
            titles: titles,
            rows: rows,
            options: series_options
          }
        end

        def ensure_equal_intervals!
          intervals = []
          sensors.each do |s|
            unless s.type < PulseMeter::Sensor::Timeline
              raise NotATimelinedSensorInWidget, "sensor `#{s.name}' is not timelined"
            end
            intervals << s.interval
          end

          unless intervals.all?{|i| i == intervals.first}
            interval_notice = sensors.map{|s| "#{s.name}: #{s.interval}"}.join(', ')
            raise DifferentSensorIntervalsInWidget, "Sensors with different intervals in a single widget: #{interval_notice}"
          end
        end

        def interval
          if sensors.empty?
            0
          else
            sensors.first.interval
          end
        end

      end

      class Area < Timeline; end
      class Line < Timeline; end
      
      class Table < Timeline 
        def data(options = {})
          res = super(options)      
          res[:series].delete(options)
          res
        end
      end
    end
  end
end