1#!/usr/bin/env python
2#
3#
4# Licensed to the Apache Software Foundation (ASF) under one
5# or more contributor license agreements.  See the NOTICE file
6# distributed with this work for additional information
7# regarding copyright ownership.  The ASF licenses this file
8# to you under the Apache License, Version 2.0 (the
9# "License"); you may not use this file except in compliance
10# with the License.  You may obtain a copy of the License at
11#
12#   http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing,
15# software distributed under the License is distributed on an
16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17# KIND, either express or implied.  See the License for the
18# specific language governing permissions and limitations
19# under the License.
20#
21#
22#
23# graph-svn-dav.py by Brian W. Fitzpatrick <fitz@red-bean.com>
24#
25# This was originally a quick hack to make a pretty picture of svn DAV servers.
26#
27# I've dropped it in Subversion's repository at the request of Karl Fogel.
28#
29# Be warned this this script has many dependencies that don't ship with Python.
30
31import sys
32import os
33import fileinput
34import datetime
35import time
36import datetime
37from matplotlib import dates
38import matplotlib
39matplotlib.use('Agg')
40from matplotlib import pylab
41import Image
42
43OUTPUT_FILE = '../../www/images/svn-dav-securityspace-survey.png'
44OUTPUT_IMAGE_WIDTH = 800
45
46STATS = [
47  ('1/1/2003', 70),
48  ('2/1/2003', 158),
49  ('3/1/2003', 222),
50  ('4/1/2003', 250),
51  ('5/1/2003', 308),
52  ('6/1/2003', 369),
53  ('7/1/2003', 448),
54  ('8/1/2003', 522),
55  ('9/1/2003', 665),
56  ('10/1/2003', 782),
57  ('11/1/2003', 969),
58  ('12/1/2003', 1009),
59  ('1/1/2004', 1162),
60  ('2/1/2004', 1307),
61  ('3/1/2004', 1424),
62  ('4/1/2004', 1792),
63  ('5/1/2004', 2113),
64  ('6/1/2004', 2502),
65  ('7/1/2004', 2941),
66  ('8/1/2004', 3863),
67  ('9/1/2004', 4174),
68  ('10/1/2004', 4187),
69  ('11/1/2004', 4783),
70  ('12/1/2004', 4995),
71  ('1/1/2005', 5565),
72  ('2/1/2005', 6505),
73  ('3/1/2005', 7897),
74  ('4/1/2005', 8751),
75  ('5/1/2005', 9793),
76  ('6/1/2005', 11534),
77  ('7/1/2005', 12808),
78  ('8/1/2005', 13545),
79  ('9/1/2005', 15233),
80  ('10/1/2005', 17588),
81  ('11/1/2005', 18893),
82  ('12/1/2005', 20278),
83  ('1/1/2006', 21084),
84  ('2/1/2006', 23861),
85  ('3/1/2006', 26540),
86  ('4/1/2006', 29396),
87  ('5/1/2006', 33001),
88  ('6/1/2006', 35082),
89  ('7/1/2006', 38939),
90  ('8/1/2006', 40672),
91  ('9/1/2006', 46525),
92  ('10/1/2006', 54247),
93  ('11/1/2006', 63145),
94  ('12/1/2006', 68988),
95  ('1/1/2007', 77027),
96  ('2/1/2007', 84813),
97  ('3/1/2007', 95679),
98  ('4/1/2007', 103852),
99  ('5/1/2007', 117267),
100  ('6/1/2007', 133665),
101  ('7/1/2007', 137575),
102  ('8/1/2007', 155426),
103  ('9/1/2007', 159055),
104  ('10/1/2007', 169939),
105  ('11/1/2007', 180831),
106  ('12/1/2007', 187093),
107  ('1/1/2008', 199432),
108  ('2/1/2008', 221547),
109  ('3/1/2008', 240794),
110  ('4/1/2008', 255520),
111  ('5/1/2008', 269478),
112  ('6/1/2008', 286614),
113  ('7/1/2008', 294579),
114  ('8/1/2008', 307923),
115  ('9/1/2008', 254757),
116  ('10/1/2008', 268081),
117  ('11/1/2008', 299071),
118  ('12/1/2008', 330884),
119  ('1/1/2009', 369719),
120  ('2/1/2009', 378434),
121  ('3/1/2009', 390502),
122  ('4/1/2009', 408658),
123  ('5/1/2009', 407044),
124  ('6/1/2009', 406520),
125  ('7/1/2009', 334276),
126  ]
127
128
129def get_date(raw_date):
130  month, day, year = map(int, raw_date.split('/'))
131  return datetime.datetime(year, month, day)
132
133
134def get_ordinal_date(date):
135  # This is the only way I can get matplotlib to do the dates right.
136  return int(dates.date2num(get_date(date)))
137
138
139def load_stats():
140  dates = [get_ordinal_date(date) for date, value in STATS]
141  counts = [x[1] for x in STATS]
142
143  return dates, counts
144
145
146def draw_graph(dates, counts):
147  ###########################################################
148  # Drawing takes place here.
149  pylab.figure(1)
150
151  ax = pylab.subplot(111)
152  pylab.plot_date(dates, counts,
153                  color='r', linestyle='-', marker='o', markersize=3)
154
155  ax.xaxis.set_major_formatter( pylab.DateFormatter('%Y') )
156  ax.xaxis.set_major_locator( pylab.YearLocator() )
157  ax.xaxis.set_minor_locator( pylab.MonthLocator() )
158  ax.set_xlim( (dates[0] - 92, dates[len(dates) - 1] + 92) )
159
160  ax.yaxis.set_major_formatter( pylab.FormatStrFormatter('%d') )
161
162  pylab.ylabel('Total # of Public DAV Servers')
163
164  lastdate = datetime.datetime.fromordinal(dates[len(dates) - 1]).strftime("%B %Y")
165  pylab.xlabel("Data as of " + lastdate)
166  pylab.title('Security Space Survey of\nPublic Subversion DAV Servers')
167  # End drawing
168  ###########################################################
169  png = open(OUTPUT_FILE, 'w')
170  pylab.savefig(png)
171  png.close()
172  os.rename(OUTPUT_FILE, OUTPUT_FILE + ".tmp.png")
173  try:
174    im = Image.open(OUTPUT_FILE + ".tmp.png", 'r')
175    (width, height) = im.size
176    print("Original size: %d x %d pixels" % (width, height))
177    scale = float(OUTPUT_IMAGE_WIDTH) / float(width)
178    width = OUTPUT_IMAGE_WIDTH
179    height = int(float(height) * scale)
180    print("Final size: %d x %d pixels" % (width, height))
181    im = im.resize((width, height), Image.ANTIALIAS)
182    im.save(OUTPUT_FILE, im.format)
183    os.unlink(OUTPUT_FILE + ".tmp.png")
184  except Exception as e:
185    sys.stderr.write("Error attempting to resize the graphic: %s\n" % (str(e)))
186    os.rename(OUTPUT_FILE + ".tmp.png", OUTPUT_FILE)
187    raise
188  pylab.close()
189
190
191if __name__ == '__main__':
192  dates, counts = load_stats()
193  draw_graph(dates, counts)
194  print("Don't forget to update ../../www/svn-dav-securityspace-survey.html!")
195