defmodule Api.Slack.Gnuplot do
alias Porcelain.Process, as: Proc
alias Porcelain.Result
require Logger
@type line :: %{title: String.t, dataset: [List.t]}
@doc """
Produces a line-graph PNG. The result is in binary.
iex> Api.Slack.Gnuplot.line_graph([%{title: "Test", dataset: [[0,1], [1,5], [2,3]]}])
iex> Api.Slack.Gnuplot.line_graph([ %{title: "A", dataset: [[0,1], [1,5], [2,3]]}, %{title: "B", dataset: [[0,3], [1,5], [2,9]]}, %{title: "C", dataset: [[0,4], [1,1], [2,6]]}, ])
"""
@spec line_graph([line]) :: binary
def line_graph(lines) do
lines
|> plot_series
|> plot
end
@doc """
Generate the inline plot & dataset configuration
to send to gnuplot
"""
def plot_series(series) do
series = do_plot_series(series)
require IEx
IEx.pry
axes = series
|> Enum.map(fn(axis) -> axis.axis end)
|> Enum.reduce("plot ", &(&2 <> &1 <> ", \n"))
datasets = series
|> Enum.map(fn(axis) -> axis.data end)
|> Enum.reduce("", &(&2 <> &1 <> "\nEOF\n"))
"""
#{axes}
#{datasets}
"""
end
def do_plot_series([series | tail]) do
[do_plot_series(series), do_plot_series(tail)]
|> Enum.reject(&(&1 == nil))
end
def do_plot_series([series = %{}]), do: do_plot_series(series)
def do_plot_series([]), do: nil
def do_plot_series(%{title: title, dataset: dataset}) do
# TODO: figure out the size of each array instead of
# hardcoding 0 and 1
data_string =
dataset
|> Enum.map(fn (set) ->
"#{Enum.fetch!(set, 0)} #{Enum.fetch!(set, 1)}"
end)
|> Enum.join("\n")
%{
axis: ~s('-' using 1:2 title "#{title}" with lp),
data: data_string
}
end
@doc """
plot will prepend generic styling rules to gnuplot, then send
the final configuration to gnuplot to process. It'll then retrieve
the response body and return it.
"""
def plot(msg) do
content = [
"# Clean basis for SVG",
"set encoding utf8",
"set terminal pngcairo enhanced transparent font 'Verdana,10' size 640,480",
"# Line width of the axes",
"set border linewidth 1.5",
"# line styles",
"set style line 1 lt 1 lc rgb '#D53E4F' # red",
"set style line 2 lt 1 lc rgb '#F46D43' # orange",
"set style line 3 lt 1 lc rgb '#FDAE61' # pale orange",
"set style line 4 lt 1 lc rgb '#FEE08B' # pale yellow-orange",
"set style line 5 lt 1 lc rgb '#E6F598' # pale yellow-green",
"set style line 6 lt 1 lc rgb '#ABDDA4' # pale green",
"set style line 7 lt 1 lc rgb '#66C2A5' # green",
"set style line 8 lt 1 lc rgb '#3288BD' # blue",
"# palette",
"set palette defined ( \\",
"0 '#D53E4F',\\",
"1 '#F46D43',\\",
"2 '#FDAE61',\\",
"3 '#FEE08B',\\",
"4 '#E6F598',\\",
"5 '#ABDDA4',\\",
"6 '#66C2A5',\\",
"7 '#3288BD'\\",
")",
msg
] |> Enum.join("\n")
|> String.replace(~s("), ~s(\\"))
cmd = ~s(echo "#{content}" | gnuplot)
IO.puts(cmd)
%Porcelain.Result{err: nil, out: out, status: 0} =
cmd
|> Porcelain.shell
File.write("/Users/john/out.png", out)
out
end
end