From ae3f27fd499c728d28cadecd6276894b6746ce66 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Wed, 26 Sep 2018 20:07:57 +0200 Subject: Create own graph type RoomSchedule to get more what I want. Based heavily on the SVG::Graph::Schedule code in the svg-graph gem, adapted and simplified for my use. --- ramaskrik-program.rb | 15 +--- room-schedule.rb | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 room-schedule.rb diff --git a/ramaskrik-program.rb b/ramaskrik-program.rb index c9044ef..3a84982 100644 --- a/ramaskrik-program.rb +++ b/ramaskrik-program.rb @@ -17,7 +17,7 @@ require 'date' require 'nokogiri' require 'open-uri' -require 'SVG/Graph/Schedule' +require 'room-schedule' class Movie attr_reader :title, :start_time, :end_time, :venue @@ -33,30 +33,23 @@ class Movie def to_s "#{title} #{start_time} - #{end_time}, #{venue}" end - - def triplet - [@title, @start_time.strftime("%d.%m.%Y %H:%M"), @end_time.strftime("%d.%m.%Y %H:%M")] - end end doc = Nokogiri::HTML(open("program.html")) ## open("https://ramaskrik.no/program/")) movies = doc.css(".kultur-type-movie").map { |movie| Movie.new(movie) } days = movies.group_by { |movie| movie.start_time.strftime("%A %d.%m.%Y") } days.each do |d, movies| - graph = SVG::Graph::Schedule.new({ + graph = SVG::Graph::RoomSchedule.new({ graph_title: d, show_graph_title: true, show_x_guidelines: true, width: 1024, - key: false, x_label_format: "%H:%M", timescale_divisions: "30 minutes", }) - venues = movies.group_by { |m| m.venue } - venues.each do |v, m| - data = m.flat_map { |m| m.triplet } - graph.add_data({ title: v, data: data }) + movies.group_by { |m| m.venue }.each do |title, data| + graph.add_data(title: title, data: data) end IO.write("#{d}-program.svg", graph.burn()) diff --git a/room-schedule.rb b/room-schedule.rb new file mode 100644 index 0000000..9268c73 --- /dev/null +++ b/room-schedule.rb @@ -0,0 +1,215 @@ +require 'date' +require 'time' +require 'SVG/Graph/Plot' + +module SVG + module Graph + class RoomSchedule < Graph + # In addition to the defaults set by Graph::initialize and + # Plot::set_defaults, sets: + # [x_label_format] '%Y-%m-%d %H:%M:%S' + # [popup_format] '%Y-%m-%d %H:%M:%S' + def set_defaults + init_with( + :x_label_format => '%Y-%m-%d %H:%M:%S', + :popup_format => '%Y-%m-%d %H:%M:%S', + :scale_x_divisions => false, + :scale_x_integers => false, + :bar_gap => true + ) + end + + # The format string use do format the X axis labels. + # See Time::strformat + attr_accessor :x_label_format + # Use this to set the spacing between dates on the axis. The value + # must be of the form + # "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?" + # + # EG: + # + # graph.timescale_divisions = "2 weeks" + # + # will cause the chart to try to divide the X axis up into segments of + # two week periods. + attr_accessor :timescale_divisions + # The formatting used for the popups. See x_label_format + attr_accessor :popup_format + attr_accessor :scale_x_divisions + attr_accessor :scale_x_integers + attr_accessor :bar_gap + + protected + + def format x, y + Time.at( x ).strftime( popup_format ) + end + + def get_x_labels + rv = get_x_values.collect { |v| v.strftime( x_label_format ) } + end + + def y_label_offset( height ) + height / -2.0 + end + + def get_y_labels + @data.map {|room| room[:title] } + end + + def draw_data + bargap = bar_gap ? (field_height < 10 ? field_height / 2 : 10) : 0 + subbar_height = field_height - bargap + + field_count = 1 + y_mod = (subbar_height / 2) + (font_size / 2) + min,max,div = x_range + scale = (@graph_width + 2 * @font_size) / (max - min).to_f + @data.each do |room| + room[:data].each do |entry| + x_start = (entry.start_time - min).to_f + x_end = (entry.end_time - min).to_f + y = @graph_height - (field_height * field_count) + bar_width = (x_end - x_start) * scale + bar_start = x_start * scale + + @graph.add_element( "rect", { + "x" => bar_start.to_s, + "y" => y.to_s, + "width" => bar_width.to_s, + "height" => subbar_height.to_s, + "class" => "fill#{field_count+1}" + }) + end + field_count += 1 + end + end + + def get_css + return <