1 # Copyright (c) 2003-2004 The Regents of The University of Michigan
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 import MySQLdb
, re
, string
33 last
= min(len(v1
), len(v2
)) - 1
34 for i
,j
in zip(v1
[0:last
], v2
[0:last
]):
38 # Special compare for last element.
39 if len(v1
) == len(v2
):
40 return cmp(v1
[last
], v2
[last
])
42 return cmp(len(v1
), len(v2
))
45 def __init__(self
, row
):
46 self
.run
= int(row
[0])
52 def __init__(self
, row
):
53 self
.stat
= int(row
[0])
60 def __init__(self
, row
):
63 self
.stat
= int(row
[0])
64 self
.run
= int(row
[1])
67 self
.data
= float(row
[4])
70 return '''Data(['%d', '%d', '%d', '%d', '%f'])''' % ( self
.stat
,
71 self
.run
, self
.x
, self
.y
, self
.data
)
73 class StatData(object):
74 def __init__(self
, row
):
75 self
.stat
= int(row
[0])
79 self
.prereq
= int(row
[5])
80 self
.precision
= int(row
[6])
84 if int(row
[4]): self
.flags |
= flags
.printable
85 if int(row
[7]): self
.flags |
= flags
.nozero
86 if int(row
[8]): self
.flags |
= flags
.nonan
87 if int(row
[9]): self
.flags |
= flags
.total
88 if int(row
[10]): self
.flags |
= flags
.pdf
89 if int(row
[11]): self
.flags |
= flags
.cdf
91 if self
.type == 'DIST' or self
.type == 'VECTORDIST':
92 self
.min = float(row
[12])
93 self
.max = float(row
[13])
94 self
.bktsize
= float(row
[14])
95 self
.size
= int(row
[15])
97 if self
.type == 'FORMULA':
98 self
.formula
= self
.db
.allFormulas
[self
.stat
]
101 def __init__(self
, name
):
106 class Result(object):
107 def __init__(self
, x
, y
):
112 def __contains__(self
, run
):
113 return run
in self
.data
115 def __getitem__(self
, run
):
116 if run
not in self
.data
:
117 self
.data
[run
] = [ [ 0.0 ] * self
.y
for i
in xrange(self
.x
) ]
118 return self
.data
[run
]
120 class Database(object):
122 self
.host
= 'zizzer.pool'
130 self
.allStatNames
= {}
136 self
.allRunNames
= {}
140 self
.allBinNames
= {}
142 self
.allFormulas
= {}
153 self
._method
= type(self
).sum
155 def get(self
, job
, stat
, system
=None):
156 run
= self
.allRunNames
.get(str(job
), None)
160 from info
import ProxyError
, scalar
, vector
, value
, values
, total
, len
161 if system
is None and hasattr(job
, 'system'):
164 if system
is not None:
165 stat
.system
= self
[system
]
168 return value(stat
, run
.run
)
170 return values(stat
, run
.run
)
176 def query(self
, sql
):
177 self
.cursor
.execute(sql
)
179 def update_dict(self
, dict):
180 dict.update(self
.stattop
)
182 def append(self
, stat
):
183 statname
= re
.sub(':', '__', stat
.name
)
184 path
= string
.split(statname
, '.')
191 if not x
.__dict
__.has_key(name
):
192 x
.__dict
__[name
] = Node(fullname
+ name
)
194 fullname
= '%s%s.' % (fullname
, name
)
197 x
.__dict
__[name
] = stat
199 self
.stattop
[pathtop
] = self
.__dict
__[pathtop
]
200 self
.statdict
[statname
] = stat
201 self
.statlist
.append(statname
)
205 self
.thedb
= MySQLdb
.connect(db
=self
.db
,
211 self
.cursor
= self
.thedb
.cursor()
213 self
.query('''select rn_id,rn_name,rn_sample,rn_user,rn_project
215 for result
in self
.cursor
.fetchall():
216 run
= RunData(result
);
217 self
.allRuns
.append(run
)
218 self
.allRunIds
[run
.run
] = run
219 self
.allRunNames
[run
.name
] = run
221 self
.query('select * from bins')
222 for id,name
in self
.cursor
.fetchall():
223 self
.allBinIds
[int(id)] = name
224 self
.allBinNames
[name
] = int(id)
226 self
.query('select sd_stat,sd_x,sd_y,sd_name,sd_descr from subdata')
227 for result
in self
.cursor
.fetchall():
228 subdata
= SubData(result
)
229 if self
.allSubData
.has_key(subdata
.stat
):
230 self
.allSubData
[subdata
.stat
].append(subdata
)
232 self
.allSubData
[subdata
.stat
] = [ subdata
]
234 self
.query('select * from formulas')
235 for id,formula
in self
.cursor
.fetchall():
236 self
.allFormulas
[int(id)] = formula
.tostring()
239 self
.query('select * from stats')
241 for result
in self
.cursor
.fetchall():
242 stat
= info
.NewStat(self
, StatData(result
))
244 self
.allStats
.append(stat
)
245 self
.allStatIds
[stat
.stat
] = stat
246 self
.allStatNames
[stat
.name
] = stat
249 # Desc: Prints all bins matching regex argument, if no argument
250 # is given all bins are returned
251 def listBins(self
, regex
='.*'):
252 print '%-50s %-10s' % ('bin name', 'id')
254 names
= self
.allBinNames
.keys()
257 id = self
.allBinNames
[name
]
258 print '%-50s %-10d' % (name
, id)
261 # Desc: Prints all runs matching a given user, if no argument
262 # is given all runs are returned
263 def listRuns(self
, user
=None):
264 print '%-40s %-10s %-5s' % ('run name', 'user', 'id')
266 for run
in self
.allRuns
:
267 if user
== None or user
== run
.user
:
268 print '%-40s %-10s %-10d' % (run
.name
, run
.user
, run
.run
)
271 # Desc: Prints all samples for a given run
272 def listTicks(self
, runs
=None):
274 print "----------------------------------------"
275 sql
= 'select distinct dt_tick from data where dt_stat=1180 and ('
284 sql
+= ' dt_run=%s' % run
.run
287 for r
in self
.cursor
.fetchall():
291 # Desc: Prints all samples for a given run
292 def retTicks(self
, runs
=None):
293 sql
= 'select distinct dt_tick from data where dt_stat=1180 and ('
301 sql
+= ' dt_run=%s' % run
.run
305 for r
in self
.cursor
.fetchall():
310 # Desc: Prints all statistics that appear in the database,
311 # the optional argument is a regular expression that can
312 # be used to prune the result set
313 def listStats(self
, regex
=None):
314 print '%-60s %-8s %-10s' % ('stat name', 'id', 'type')
319 rx
= re
.compile(regex
)
321 stats
= [ stat
.name
for stat
in self
.allStats
]
324 stat
= self
.allStatNames
[stat
]
325 if rx
== None or rx
.match(stat
.name
):
326 print '%-60s %-8s %-10s' % (stat
.name
, stat
.stat
, stat
.type)
329 # Desc: Prints all statistics that appear in the database,
330 # the optional argument is a regular expression that can
331 # be used to prune the result set
332 def listFormulas(self
, regex
=None):
333 print '%-60s %s' % ('formula name', 'formula')
338 rx
= re
.compile(regex
)
340 stats
= [ stat
.name
for stat
in self
.allStats
]
343 stat
= self
.allStatNames
[stat
]
344 if stat
.type == 'FORMULA' and (rx
== None or rx
.match(stat
.name
)):
345 print '%-60s %s' % (stat
.name
, self
.allFormulas
[stat
.stat
])
347 def getStat(self
, stats
):
348 if type(stats
) is not list:
353 if type(stat
) is int:
354 ret
.append(self
.allStatIds
[stat
])
356 if type(stat
) is str:
357 rx
= re
.compile(stat
)
358 for stat
in self
.allStats
:
359 if rx
.match(stat
.name
):
363 def getBin(self
, bins
):
364 if type(bins
) is not list:
371 elif type(bin
) is str:
372 ret
.append(self
.allBinNames
[bin
])
374 for name
,id in self
.allBinNames
.items():
380 def getNotBin(self
, bin
):
382 for bin
in getBin(bin
):
386 for bin
in self
.allBinIds
.keys():
387 if not map.has_key(bin
):
392 #########################################
395 def inner(self
, op
, stat
, bins
, ticks
, group
=False):
397 sql
+= 'dt_stat as stat, '
398 sql
+= 'dt_run as run, '
402 sql
+= 'dt_tick as tick, '
403 sql
+= '%s(dt_data) as data ' % op
407 if isinstance(stat
, list):
408 val
= ' or '.join([ 'dt_stat=%d' % s
.stat
for s
in stat
])
411 sql
+= ' dt_stat=%d' % stat
.stat
413 if self
.runs
!= None and len(self
.runs
):
414 val
= ' or '.join([ 'dt_run=%d' % r
for r
in self
.runs
])
415 sql
+= ' and (%s)' % val
417 if bins
!= None and len(bins
):
418 val
= ' or '.join([ 'dt_bin=%d' % b
for b
in bins
])
419 sql
+= ' and (%s)' % val
421 if ticks
!= None and len(ticks
):
422 val
= ' or '.join([ 'dt_tick=%d' % s
for s
in ticks
])
423 sql
+= ' and (%s)' % val
425 sql
+= ' group by dt_stat,dt_run,dt_x,dt_y'
430 def outer(self
, op_out
, op_in
, stat
, bins
, ticks
):
431 sql
= self
.inner(op_in
, stat
, bins
, ticks
, True)
432 sql
= 'select stat,run,x,y,%s(data) from (%s) as tb ' % (op_out
, sql
)
433 sql
+= 'group by stat,run,x,y'
437 # Desc: given a run, a stat and an array of samples and bins,
438 # sum all the bins and then get the standard deviation of the
439 # samples for non-binned runs. This will just return the average
440 # of samples, however a bin array still must be passed
441 def sum(self
, stat
, bins
, ticks
):
442 return self
.inner('sum', stat
, bins
, ticks
)
445 # Desc: given a run, a stat and an array of samples and bins,
446 # sum all the bins and then average the samples for non-binned
447 # runs this will just return the average of samples, however
448 # a bin array still must be passed
449 def avg(self
, stat
, bins
, ticks
):
450 return self
.outer('avg', 'sum', stat
, bins
, ticks
)
453 # Desc: given a run, a stat and an array of samples and bins,
454 # sum all the bins and then get the standard deviation of the
455 # samples for non-binned runs. This will just return the average
456 # of samples, however a bin array still must be passed
457 def stdev(self
, stat
, bins
, ticks
):
458 return self
.outer('stddev', 'sum', stat
, bins
, ticks
)
460 def __setattr__(self
, attr
, value
):
461 super(Database
, self
).__setattr
__(attr
, value
)
466 self
._method
= self
.sum
468 self
._method
= self
.avg
469 elif value
== 'stdev':
470 self
._method
= self
.stdev
472 raise AttributeError, "can only set get to: sum | avg | stdev"
474 def data(self
, stat
, bins
=None, ticks
=None):
479 sql
= self
._method
(self
, stat
, bins
, ticks
)
485 for x
in self
.cursor
.fetchall():
487 if not runs
.has_key(data
.run
):
489 if not runs
[data
.run
].has_key(data
.x
):
490 runs
[data
.run
][data
.x
] = {}
492 xmax
= max(xmax
, data
.x
)
493 ymax
= max(ymax
, data
.y
)
494 runs
[data
.run
][data
.x
][data
.y
] = data
.data
496 results
= Result(xmax
+ 1, ymax
+ 1)
497 for run
,data
in runs
.iteritems():
498 result
= results
[run
]
499 for x
,ydata
in data
.iteritems():
500 for y
,data
in ydata
.iteritems():
504 def __getitem__(self
, key
):
505 return self
.stattop
[key
]