3
# -*- coding: utf-8 -*-
6
# Copyright: 2006 by Dalibo <dalibo.com>
7
# Copyright: 2007 Dimitri Fontaine <dim@tapoueh.org>
8
# Created: 2006 by Dimitri Fontaine <dim@tapoueh.org>
10
# Modified: 2008 by Nicolas Niclausse
12
# This program is free software; you can redistribute it and/or modify
13
# it under the terms of the GNU General Public License as published by
14
# the Free Software Foundation; either version 2 of the License, or
15
# (at your option) any later version.
17
# This program is distributed in the hope that it will be useful,
18
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
# GNU General Public License for more details.
22
# You should have received a copy of the GNU General Public License
23
# along with this program; if not, write to the Free Software
24
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
26
# In addition, as a special exception, you have the permission to
27
# link the code of this program with any library released under
28
# the EPL license and distribute linked combinations including
31
# A plotter for tsung text generated data
35
LIBDIR = "@EXPANDED_LIBDIR@/tsung"
36
SHAREDIR = "@EXPANDED_SHAREDIR@"
38
CONF = "http.plots.en.conf"
39
SYS_STATS_CONF = os.path.join(SHAREDIR, 'tsung_plotter', 'stats.conf')
41
sys.path.append(LIBDIR)
43
from tsung_plotter.tsung import TsungLog
44
from ConfigParser import ConfigParser
49
""" The ploting class, using matplotlib """
58
styles = ['b-', 'r-', 'g-', 'c-', 'y^', 'kv'],
59
colors = ['b', 'r', 'g', 'c', 'y', 'k'],
65
plottype = 'lineplot', # can be lineplot or bar
66
outdir = '/tmp/tsung-plotter',
69
# Only used if no plot type given
74
self.legends = legends
75
self.position = position
77
self.thumb_dpi = tn_dpi
78
self.xfactor = xfactor
79
self.yfactor = yfactor
81
self.plottype = plottype
85
self.imgtype = imgtype
87
def plot(self, stats, dataset):
88
""" draw a simple timeline or bar plots from two dataset """
90
if self.plottype == "lineplot":
91
# prepare matplotlib graph
94
ax.set_yscale(self.yscale)
98
# get give type data for plotting
100
for (name, stat) in stats:
101
pdata = data.stat(name, stat)
102
# we want timestamp sorted data to plot
105
values = [pdata[t] / self.yfactor[nstats%len(self.yfactor)] for t in ts]
107
if self.xfactor not in [1, "1"]:
108
ts = [x / self.xfactor for x in ts]
110
# Now use matplotlib to do the plotting
111
plot(ts, values, self.styles[count%len(self.styles)])
114
# Now setup the legend
118
legend(self.legends, loc=self.position)
120
# we want to draw a grid
123
filename = os.path.join(self.outdir,
124
"%s.%s" % (self.name, self.imgtype))
125
thumbnail = os.path.join(self.outdir,
126
"%s_tn.%s" % (self.name, self.imgtype))
128
savefig(filename, dpi=self.dpi, orientation='portrait')
129
savefig(thumbnail, dpi=self.thumb_dpi, orientation='portrait')
132
# box chart for gmean
134
width = 1.0/len(dataset)
142
ax.set_yscale(self.yscale)
145
for (name, stat) in stats:
146
pdata = data.stat(name, stat)
149
# the data we use is the latest value:
150
current[count] = pdata.values()[size-1] / self.yfactor[nstats]
151
ax.bar(ind+count*width, ( current[count] ), width,
152
color=self.colors[count] )
153
# get percent of increase/decrease:
155
percent.append(str(round(-100 + current[count] / current[0] * 100)) + "% ")
157
percent.append("reference")
158
currenttick=(0.5 + count)/nbox
159
ticksp.append(currenttick)
163
boxchart = os.path.join(self.outdir,
164
"%s.%s" % (self.name, self.imgtype))
165
boxchart_thumb = os.path.join(self.outdir,
166
"%s_tn.%s" % (self.name, self.imgtype))
168
legend(self.legends, loc=self.position)
169
ax.set_xticks(ticksp)
170
ax.set_xticklabels(percent)
172
savefig(boxchart, dpi=self.dpi, orientation='portrait')
173
savefig(boxchart_thumb, dpi=self.thumb_dpi, orientation='portrait')
177
def main(conffile, logs, legends, outdir, verbose):
178
""" Produce plots from given """
180
dataset = [d for f, d in logs]
182
config = ConfigParser()
183
config.read(conffile)
185
for s in config.sections():
186
p = Plot(name = s, outdir = outdir)
189
for d, v in config.defaults().items():
196
# conf: 200.count, 400.count
197
# result: [["200", "count"], ["400", "count"]]
199
stats = [x.strip().rsplit('.', 1)
200
for x in config.get(s, 'stats').split(' ')]
202
print 'error: unable to read plot "%s" stats' % s
205
if config.has_option(s, 'styles'):
206
p.styles = [x.strip() for x in config.get(s, 'styles').split(' ')]
208
if config.has_option(s, 'legend'):
209
# this is the legend prefix$
212
clegends = config.get(s, 'legend').decode(encoding).split(',')
215
l += ['%s %s' % (x.strip(), legends[count]) \
220
if config.has_option(s, 'position'):
221
# legend position according to matplotlib standard values (1-10)
222
val = config.get(s, 'position').decode(encoding)
224
p.position = int(val)
229
if config.has_option(s, 'yfactor'):
231
p.__dict__['yfactor'] = map(float,config.get(s, 'yfactor').decode(encoding).split(','))
233
print 'warning: %s yfactor not a number: %s' \
234
% (p.name, config.get(s, yfactor))
235
# Text parameters - to decode into specified encoding
236
for attr in ['title', 'xlabel', 'ylabel', 'plottype', 'yscale']:
237
if config.has_option(s, attr):
238
cstring = config.get(s, attr).decode(encoding)
239
p.__dict__[attr] = cstring
241
# Numerical parameters
242
for attr in ['xfactor', 'dpi', 'tn_dpi']:
243
if config.has_option(s, attr):
245
p.__dict__[attr] = config.getfloat(s, attr)
247
print 'warning: %s %s not a number: %s' \
248
% (p.name, attr, config.get(s, attr))
250
outfile = p.plot(stats, dataset)
253
print 'Generated plot %s' % outfile
255
if __name__ == "__main__":
256
from optparse import OptionParser
258
parser = OptionParser()
259
parser.add_option("-c", "--config", dest="config", default=None,
260
help="configuration file")
261
parser.add_option("-d", "--outdir", dest="outdir", default="/tmp/tsung",
262
help="output dir where to save plots (/tmp/tsung)")
263
parser.add_option("-v", action="store_true", dest="verbose", default=False,
266
(options, args) = parser.parse_args()
268
if options.config is None:
269
userconf = os.path.join(os.environ['HOME'], USERDIR, CONF)
270
if os.access(userconf, os.R_OK):
273
config = os.path.join(SHAREDIR, 'tsung_plotter', CONF)
275
config = options.config
278
print 'Using %s configuration file' % config
280
if not os.access(config, os.R_OK):
281
print "can't read configuration file: %s" % config
284
# FIXME: error control
285
# OSError: [Errno 17] Le fichier existe.: '/tmp/tsung'
287
os.makedirs(options.outdir)
291
# args are legend then file, any times wanted by user
292
if len(args) % 2 != 0:
293
print "error: please provide legend and tsung log filename"
309
print 'Using %s stats configuration file' % SYS_STATS_CONF
312
for logfile in files:
313
if not os.access(logfile, os.R_OK):
314
print "error: unable to read file %s" % logfile
318
print 'Parsing Tsung log file', logfile
319
logs.append((logfile, TsungLog(SYS_STATS_CONF, logfile)))
321
if len(logs) != len(args) / 2:
322
print 'error while parsing files (%d != %d)' % (len(logs),
326
main(config, logs, legends, options.outdir, options.verbose)