switches to choose plotter
[git-chart] / git-chart
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use POSIX qw(ceil);
6 use Getopt::Long;
7
8 sub usage() {
9         print "Usage: git chart\n";
10 }
11
12 sub gather_data($) {
13         my $options = shift;
14         my @dataset;
15
16         print "Gathering data ...\n";
17
18         my $day=$options->{from};
19         my $to =$options->{to};
20         my $step=$options->{step};
21         my $max=$options->{max} || 0;
22
23         while ($day < $to) {
24                 my $next = $day + $step;
25                 my $val = `git log --pretty=%s --since="$next days ago" --until="$day days ago" | wc -l`;
26                 chomp $val;
27                 push @dataset, $val;
28                 $max = $val if $max < $val;
29                 $day = $next;
30         }
31
32         $options->{max} = $max;
33
34         print "...done\n";
35
36         return \@dataset;
37 }
38
39 # functions to plot the datasets.
40 # each function can be called with either one or two parameters.
41 # when called with two parameters, the first is assumed to be the dataset, and the second the options
42 # (array and hash ref respectively).
43 # when called with a single parameter, it is assumed to be an options hash ref, and the dataset is 
44 # created by calling gather_data with the passed options.
45
46 # google chart API
47 # TODO needs a lot of customization
48 sub google_chart($;$) {
49         my $dataset = shift;
50         my $options = shift;
51         if (! defined $options) {
52                 $options = $dataset;
53                 $dataset = gather_data($options);
54         }
55
56         my $height=$options->{chart_height};
57         my $max = $options->{max};
58         my $from = $options->{from};
59         my $to = $options->{to};
60         my $step = $options->{step};
61         my $width=($step < 20 ? 20 : $step)*@$dataset;
62
63         my $url="https://chart.googleapis.com/chart?chs=${width}x${height}&cht=bvg&chd=t:%s&chds=0,$max&chbh=a&chxt=y,x&chxr=0,0,$max|1,$to,$from,-$step";
64
65         my $launch = sprintf $url, join(",",reverse @$dataset);
66         # print $launch, "\n";
67         `git web--browse "$launch"`
68 }
69
70 # gnuplot
71 sub gnuplot_chart($;$) {
72         my $dataset = shift;
73         my $options = shift;
74         if (! defined $options) {
75                 $options = $dataset;
76                 $dataset = gather_data($options);
77         }
78
79         my $max = $options->{max};
80         my $from = $options->{from};
81         my $to = $options->{to};
82         my $step = $options->{step};
83
84         # TODO allow customization
85         # in particular, detect (lack of) display and set term to dumb accordingly
86         my $termcmd = $options->{gnuplot_term};
87         my $plotsetup = $options->{gnuplot_setup};
88         $plotsetup .="\nset yrange [0:$max]\nset xrange [$from:$to]";
89         my $plotstyle = $options->{gnuplot_style};
90         my $plotoptions = $options->{gnuplot_plotwith};
91         my $data = join("\n", reverse @$dataset);
92
93         open my $gp, "|gnuplot -persist";
94
95         print $gp <<GPCMD
96         $termcmd
97         $plotsetup
98         $plotstyle
99         plot "-" $plotoptions
100         $data
101 GPCMD
102
103 }
104
105 sub parse_from($) {
106         my $from = shift;
107         if ($from =~/^\d+$/) {
108                 return 0 + $from;
109         } else {
110                 # TODO
111                 warn "non-numeric from not supported yet\n";
112         }
113 }
114
115 sub parse_to($) {
116         my $to = shift;
117         if ($to =~/^\d+$/) {
118                 return 0 + $to;
119         } else {
120                 # TODO
121                 warn "non-numeric to not supported yet\n";
122         }
123 }
124
125 sub parse_step($) {
126         my $step = shift;
127         if ($step =~/^\d+$/) {
128                 return 0 + $step;
129         } else {
130                 return 1 if $step eq 'daily';
131                 return 7 if $step eq 'weekly';
132                 return 30 if $step eq 'monthly';
133         }
134 }
135
136 # some defaults
137 my %options = (
138         from => 0,
139         to => 31,
140         step => 1,
141         # charting/plotting options
142         plotter => \&gnuplot_chart,
143         chart_height => 144,
144         gnuplot_term => '',
145         gnuplot_setup => "set nokey",
146         gnuplot_style => 'set style fill solid 1.0 border -1',
147         gnuplot_plotwith => 'with boxes',
148 );
149
150 my $daily=0;
151 my $weekly=0;
152 my $monthly=0;
153
154 my $from=0;
155 my $to=0;
156 my $step=0;
157
158 GetOptions(
159         daily => \$daily,
160         weekly => \$weekly,
161         monthly => \$monthly,
162         'from=s' => \$from,
163         'to=s' => \$to,
164         'step=s' => \$step,
165         google => sub { $options{plotter} = \&google_chart },
166         gnuplot => sub { $options{plotter} = \&gnuplot_chart },
167 );
168
169 $options{from} = parse_from($from) if $from;
170
171 if ($monthly) {
172         $options{to} = $options{from} + 365;
173         $options{step} = 30;
174 } elsif ($weekly) {
175         $options{to} = $options{from} + 6*30;
176         $options{step} = 30;
177 } elsif ($daily) {
178         $options{to} = $options{from} + 31;
179         $options{step} = 1;
180 }
181
182 $options{to} = parse_to($to) if $to;
183 $options{step} = parse_step($step) if $step;
184
185 die "step must be strictly positive!" unless $options{step} > 0;
186
187 $options{plotter}->(\%options);