1 |
commit: d1300fc92439e26b6941eec62ec888329835ee77 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Mon Jul 29 14:53:05 2013 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Mon Jul 29 14:53:05 2013 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=d1300fc9 |
7 |
|
8 |
write stats to round-robin database (rrdtool) |
9 |
|
10 |
This commit makes (/allows to/) stats data persistent by adding them to a db, |
11 |
which can later be used to draw graphs etc. |
12 |
|
13 |
--- |
14 |
roverlay/stats/abstract.py | 37 +----- |
15 |
roverlay/stats/collector.py | 156 +++++--------------------- |
16 |
roverlay/stats/dbcollector.py | 74 ++++++++++++ |
17 |
roverlay/stats/rrd.py | 63 +++++++++++ |
18 |
roverlay/stats/{collector.py => visualize.py} | 91 +++------------ |
19 |
5 files changed, 189 insertions(+), 232 deletions(-) |
20 |
|
21 |
diff --git a/roverlay/stats/abstract.py b/roverlay/stats/abstract.py |
22 |
index 6d0575a..68728f4 100644 |
23 |
--- a/roverlay/stats/abstract.py |
24 |
+++ b/roverlay/stats/abstract.py |
25 |
@@ -9,34 +9,7 @@ from __future__ import division |
26 |
import collections |
27 |
import time |
28 |
|
29 |
-class MethodNotImplemented ( NotImplementedError ): |
30 |
- def __init__ ( self, obj, method ): |
31 |
- super ( MethodNotImplemented, self ).__init__ ( |
32 |
- "{n}.{f}()".format ( n=obj.__class__.__name__, f=method ) |
33 |
- ) |
34 |
- # --- end of __init__ (...) --- |
35 |
- |
36 |
-# --- end of MethodNotImplemented --- |
37 |
- |
38 |
-class StatsVisualizer ( object ): |
39 |
- |
40 |
- def __init__ ( self, stats ): |
41 |
- super ( StatsVisualizer, self ).__init__() |
42 |
- self.stats = stats |
43 |
- self.lines = None |
44 |
- |
45 |
- self.prepare() |
46 |
- # --- end of __init__ (...) --- |
47 |
- |
48 |
- def prepare ( self ): |
49 |
- raise MethodNotImplemented ( self, 'prepare' ) |
50 |
- # --- end of prepare (...) --- |
51 |
- |
52 |
- def __str__ ( self ): |
53 |
- return '\n'.join ( self.lines ) |
54 |
- # --- end of __str__ (...) --- |
55 |
- |
56 |
-# --- end of StatsVisualizer --- |
57 |
+from roverlay.util.objects import MethodNotImplementedError |
58 |
|
59 |
|
60 |
class RoverlayStatsBase ( object ): |
61 |
@@ -66,7 +39,7 @@ class RoverlayStatsBase ( object ): |
62 |
self.merge_members ( other, my_cls._MEMBERS ) |
63 |
|
64 |
else: |
65 |
- raise MethodNotImplemented ( self, 'merge_with' ) |
66 |
+ raise MethodNotImplementedError ( self, 'merge_with' ) |
67 |
# --- end of merge_with (...) --- |
68 |
|
69 |
def merge_members ( self, other, members ): |
70 |
@@ -86,7 +59,7 @@ class RoverlayStatsBase ( object ): |
71 |
if int ( member ) != 0: |
72 |
return member |
73 |
else: |
74 |
- raise MethodNotImplemented ( self, 'has_nonzero' ) |
75 |
+ raise MethodNotImplementedError ( self, 'has_nonzero' ) |
76 |
# --- end of has_nonzero (...) --- |
77 |
|
78 |
def reset_members ( self ): |
79 |
@@ -98,7 +71,7 @@ class RoverlayStatsBase ( object ): |
80 |
if hasattr ( self.__class__, '_MEMBERS' ): |
81 |
self.reset_members() |
82 |
else: |
83 |
- raise MethodNotImplemented ( self, 'reset' ) |
84 |
+ raise MethodNotImplementedError ( self, 'reset' ) |
85 |
# --- end of reset (...) --- |
86 |
|
87 |
def get_description_str ( self ): |
88 |
@@ -122,7 +95,7 @@ class RoverlayStatsBase ( object ): |
89 |
if ret: |
90 |
return ret |
91 |
else: |
92 |
- raise MethodNotImplemented ( self, '__str__' ) |
93 |
+ raise MethodNotImplementedError ( self, '__str__' ) |
94 |
# --- end of __str__ (...) --- |
95 |
|
96 |
# --- end of RoverlayStatsBase --- |
97 |
|
98 |
diff --git a/roverlay/stats/collector.py b/roverlay/stats/collector.py |
99 |
index 3e3dee7..30b0e76 100644 |
100 |
--- a/roverlay/stats/collector.py |
101 |
+++ b/roverlay/stats/collector.py |
102 |
@@ -4,10 +4,15 @@ |
103 |
# Distributed under the terms of the GNU General Public License; |
104 |
# either version 2 of the License, or (at your option) any later version. |
105 |
|
106 |
-import collections |
107 |
+import time |
108 |
+ |
109 |
+import roverlay.config.static |
110 |
|
111 |
from . import abstract |
112 |
from . import base |
113 |
+from . import dbcollector |
114 |
+from . import visualize |
115 |
+from . import rrd |
116 |
|
117 |
|
118 |
class StatsCollector ( abstract.RoverlayStatsBase ): |
119 |
@@ -57,13 +62,29 @@ class StatsCollector ( abstract.RoverlayStatsBase ): |
120 |
# --- end of get_net_gain (...) --- |
121 |
|
122 |
def __init__ ( self ): |
123 |
+ super ( StatsCollector, self ).__init__() |
124 |
+ |
125 |
self.time = abstract.TimeStats ( "misc time stats" ) |
126 |
self.distmap = base.DistmapStats() |
127 |
self.overlay = base.OverlayStats() |
128 |
self.overlay_creation = base.OverlayCreationStats() |
129 |
self.repo = base.RepoStats() |
130 |
+ self.db_collector = None |
131 |
+ self._database = None |
132 |
# --- end of __init__ (...) --- |
133 |
|
134 |
+ def setup_database ( self, config=None ): |
135 |
+ conf = ( |
136 |
+ config if config is not None else roverlay.config.static.access() |
137 |
+ ) |
138 |
+ |
139 |
+ self.db_collector = dbcollector.StatsDBCollector ( self ) |
140 |
+ self._database = rrd.StatsDB ( |
141 |
+ conf.get_or_fail ( "RRD_DB.file" ), self.db_collector |
142 |
+ ) |
143 |
+ self._database.create_if_missing() |
144 |
+ # --- end of setup_database (...) --- |
145 |
+ |
146 |
def gen_str ( self ): |
147 |
yield "{success}, overall {osuccess}".format ( |
148 |
success = self.get_success_ratio(), |
149 |
@@ -77,136 +98,17 @@ class StatsCollector ( abstract.RoverlayStatsBase ): |
150 |
# --- end of gen_str (...) --- |
151 |
|
152 |
def get_creation_str ( self ): |
153 |
- return str ( CreationStatsVisualizer ( self ) ) |
154 |
+ return str ( visualize.CreationStatsVisualizer ( self ) ) |
155 |
# --- end of to_creation_str (...) --- |
156 |
|
157 |
-# --- end of StatsCollector --- |
158 |
- |
159 |
- |
160 |
-class CreationStatsVisualizer ( abstract.StatsVisualizer ): |
161 |
- |
162 |
- def prepare ( self ): |
163 |
- EMPTY_LINE = "" |
164 |
- |
165 |
- pkg_count = self.stats.repo.pkg_count |
166 |
- pkg_queued = self.stats.overlay_creation.pkg_queued |
167 |
- pkg_fail = self.stats.overlay_creation.pkg_fail |
168 |
- pkg_success = self.stats.overlay_creation.pkg_success |
169 |
- ebuild_delta = self.stats.get_net_gain() |
170 |
- revbumps = self.stats.overlay.revbump_count |
171 |
- |
172 |
- max_number_len = min ( |
173 |
- len ( str ( int ( k ) ) ) for k in ( |
174 |
- pkg_queued, pkg_fail, pkg_success |
175 |
- ) |
176 |
- ) |
177 |
- max_number_len = min ( 5, max_number_len ) |
178 |
- |
179 |
- success_ratio = self.stats.get_success_ratio() |
180 |
- overall_success_ratio = self.stats.get_overall_success_ratio() |
181 |
- |
182 |
+ def write_db ( self ): |
183 |
+ self.db_collector.update() |
184 |
+ self._database.update() |
185 |
+ self._database.commit() |
186 |
+ # --- end of write_db (...) --- |
187 |
|
188 |
- timestats = ( |
189 |
- ( 'scan_overlay', self.stats.overlay.scan_time.get_total_str() ), |
190 |
- ( 'add_packages', self.stats.repo.queue_time.get_total_str() ), |
191 |
- ( |
192 |
- 'ebuild_creation', |
193 |
- self.stats.overlay_creation.creation_time.get_total_str() |
194 |
- ), |
195 |
- ( 'write_overlay', self.stats.overlay.write_time.get_total_str() ), |
196 |
- ) |
197 |
- |
198 |
- try: |
199 |
- max_time_len = max ( len(k) for k, v in timestats if v is not None ) |
200 |
- except ValueError: |
201 |
- # empty sequence -> no timestats |
202 |
- max_time_len = -1 |
203 |
- else: |
204 |
- # necessary? |
205 |
- max_time_len = min ( 39, max_time_len ) |
206 |
- |
207 |
- |
208 |
- # create lines |
209 |
- lines = collections.deque() |
210 |
- unshift = lines.appendleft |
211 |
- append = lines.append |
212 |
- |
213 |
- numstats = lambda k, s: "{num:<{l}d} {s}".format ( |
214 |
- num=int ( k ), s=s, l=max_number_len |
215 |
- ) |
216 |
- |
217 |
- |
218 |
- append ( |
219 |
- 'success ratio {s_i:.2%} (overall {s_o:.2%})'.format ( |
220 |
- s_i = success_ratio.get_ratio(), |
221 |
- s_o = overall_success_ratio.get_ratio() |
222 |
- ) |
223 |
- ) |
224 |
- append ( EMPTY_LINE ) |
225 |
- |
226 |
- append ( |
227 |
- "{e:+d} ebuilds ({r:d} revbumps)".format ( |
228 |
- e=ebuild_delta, r=int ( revbumps ) |
229 |
- ) |
230 |
- ) |
231 |
- append ( EMPTY_LINE ) |
232 |
- |
233 |
- if int ( pkg_count ) != int ( pkg_queued ): |
234 |
- append ( numstats ( |
235 |
- pkg_queued, |
236 |
- '/ {n:d} packages added to the ebuild creation queue'.format ( |
237 |
- n=int ( pkg_count ) |
238 |
- ) |
239 |
- ) ) |
240 |
- else: |
241 |
- append ( numstats ( |
242 |
- pkg_queued, 'packages added to the ebuild creation queue' |
243 |
- ) ) |
244 |
- |
245 |
- append ( numstats ( |
246 |
- pkg_success, 'packages passed ebuild creation' |
247 |
- ) ) |
248 |
- |
249 |
- append ( numstats ( |
250 |
- pkg_fail, 'packages failed ebuild creation' |
251 |
- ) ) |
252 |
- |
253 |
- if pkg_fail.has_details() and int ( pkg_fail ) != 0: |
254 |
- append ( EMPTY_LINE ) |
255 |
- append ( "Details for ebuild creation failure:" ) |
256 |
- details = sorted ( |
257 |
- ( ( k, int(v) ) for k, v in pkg_fail.iter_details() ), |
258 |
- key=lambda kv: kv[1] |
259 |
- ) |
260 |
- dlen = len ( str ( max ( details, key=lambda kv: kv[1] ) [1] ) ) |
261 |
- |
262 |
- for key, value in details: |
263 |
- append ( "* {v:>{l}d}: {k}".format ( k=key, v=value, l=dlen ) ) |
264 |
- # -- end if <have pkg_fail details> |
265 |
- |
266 |
- if max_time_len > 0: |
267 |
- # or >= 0 |
268 |
- append ( EMPTY_LINE ) |
269 |
- for k, v in timestats: |
270 |
- if v is not None: |
271 |
- append ( |
272 |
- "time for {0:<{l}} : {1}".format ( k, v, l=max_time_len ) |
273 |
- ) |
274 |
- # -- end if timestats |
275 |
- |
276 |
- |
277 |
- append ( EMPTY_LINE ) |
278 |
- |
279 |
- # add header/footer line(s) |
280 |
- max_line_len = 2 + min ( 78, max ( len(s) for s in lines ) ) |
281 |
- unshift ( |
282 |
- "{0:-^{1}}\n".format ( " Overlay creation stats ", max_line_len ) |
283 |
- ) |
284 |
- append ( max_line_len * '-' ) |
285 |
+# --- end of StatsCollector --- |
286 |
|
287 |
- self.lines = lines |
288 |
- # --- end of gen_str (...) --- |
289 |
-# --- end of CreationStatsVisualizer --- |
290 |
|
291 |
static = StatsCollector() |
292 |
StatsCollector._instance = static |
293 |
|
294 |
diff --git a/roverlay/stats/dbcollector.py b/roverlay/stats/dbcollector.py |
295 |
new file mode 100644 |
296 |
index 0000000..402e64d |
297 |
--- /dev/null |
298 |
+++ b/roverlay/stats/dbcollector.py |
299 |
@@ -0,0 +1,74 @@ |
300 |
+# R overlay -- stats collection, prepare data for db storage |
301 |
+# -*- coding: utf-8 -*- |
302 |
+# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
303 |
+# Distributed under the terms of the GNU General Public License; |
304 |
+# either version 2 of the License, or (at your option) any later version. |
305 |
+ |
306 |
+import collections |
307 |
+import weakref |
308 |
+ |
309 |
+class StatsDBCollector ( object ): |
310 |
+ VERSION = 0 |
311 |
+ |
312 |
+ # 'pc' := package count(er), 'ec' := ebuild _ |
313 |
+ NUMSTATS_KEYS = ( |
314 |
+ 'pc_repo', 'pc_distmap', 'pc_filtered', 'pc_queued', 'pc_success', |
315 |
+ 'pc_fail', 'pc_fail_empty', 'pc_fail_dep', 'pc_fail_selfdep', |
316 |
+ 'pc_fail_err', |
317 |
+ 'ec_pre', 'ec_post', 'ec_written', 'ec_revbump', |
318 |
+ ) |
319 |
+ NUMSTATS = collections.namedtuple ( |
320 |
+ "numstats", ' '.join ( NUMSTATS_KEYS ) |
321 |
+ ) |
322 |
+ |
323 |
+ TIMESTATS_KEYS = () |
324 |
+ TIMESTATS = collections.namedtuple ( |
325 |
+ "timestats", ' '.join ( TIMESTATS_KEYS ) |
326 |
+ ) |
327 |
+ |
328 |
+ def __init__ ( self, stats ): |
329 |
+ super ( StatsDBCollector, self ).__init__() |
330 |
+ self.stats = weakref.ref ( stats ) |
331 |
+ self._collected_stats = None |
332 |
+ # --- end of __init__ (...) --- |
333 |
+ |
334 |
+ def update ( self ): |
335 |
+ self._collected_stats = self.make_all() |
336 |
+ # --- end of update (...) --- |
337 |
+ |
338 |
+ def make_numstats ( self ): |
339 |
+ stats = self.stats() |
340 |
+ ov_create = stats.overlay_creation |
341 |
+ ov = stats.overlay |
342 |
+ |
343 |
+ return self.__class__.NUMSTATS ( |
344 |
+ pc_repo = int ( stats.repo.pkg_count ), |
345 |
+ pc_distmap = int ( stats.distmap.pkg_count ), |
346 |
+ pc_filtered = int ( ov_create.pkg_filtered ), |
347 |
+ pc_queued = int ( ov_create.pkg_queued ), |
348 |
+ pc_success = int ( ov_create.pkg_success ), |
349 |
+ pc_fail = int ( ov_create.pkg_fail ), |
350 |
+ pc_fail_empty = ov_create.pkg_fail.get ( 'empty_desc' ), |
351 |
+ pc_fail_dep = ov_create.pkg_fail.get ( 'unresolved_deps' ), |
352 |
+ pc_fail_selfdep = ov_create.pkg_fail.get ( 'bad_selfdeps' ), |
353 |
+ pc_fail_err = ov_create.pkg_fail.get ( 'exception' ), |
354 |
+ ec_pre = int ( ov.ebuilds_scanned ), |
355 |
+ ec_post = int ( ov.ebuild_count ), |
356 |
+ ec_written = int ( ov.ebuilds_written ), |
357 |
+ ec_revbump = int ( ov.revbump_count ), |
358 |
+ ) |
359 |
+ # --- end of make_numstats (...) --- |
360 |
+ |
361 |
+ def make_timestats ( self ): |
362 |
+ return () |
363 |
+ # --- end of make_timestats (...) --- |
364 |
+ |
365 |
+ def make_all ( self ): |
366 |
+ return self.make_numstats() + self.make_timestats() |
367 |
+ # --- end of make_all (...) --- |
368 |
+ |
369 |
+ def get_all ( self ): |
370 |
+ return self._collected_stats |
371 |
+ # --- end of get_all (...) --- |
372 |
+ |
373 |
+# --- end of StatsDBCollector #v0 --- |
374 |
|
375 |
diff --git a/roverlay/stats/rrd.py b/roverlay/stats/rrd.py |
376 |
new file mode 100644 |
377 |
index 0000000..66da434 |
378 |
--- /dev/null |
379 |
+++ b/roverlay/stats/rrd.py |
380 |
@@ -0,0 +1,63 @@ |
381 |
+# R overlay -- stats collection, rrd database (using rrdtool) |
382 |
+# -*- coding: utf-8 -*- |
383 |
+# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
384 |
+# Distributed under the terms of the GNU General Public License; |
385 |
+# either version 2 of the License, or (at your option) any later version. |
386 |
+ |
387 |
+ |
388 |
+# NOT using rrdtool's python bindings as they're available for python 2 only |
389 |
+ |
390 |
+import roverlay.db.rrdtool |
391 |
+from roverlay.db.rrdtool import RRDVariable, RRDArchive |
392 |
+ |
393 |
+ |
394 |
+class StatsDB ( roverlay.db.rrdtool.RRD ): |
395 |
+ |
396 |
+ # default step |
397 |
+ STEP = 300 |
398 |
+ |
399 |
+ def __init__ ( self, filepath, collector, step=None ): |
400 |
+ # COULDFIX: |
401 |
+ # vars / RRA creation is only necessary when creating a new database |
402 |
+ # |
403 |
+ self.collector = collector |
404 |
+ self.rrd_vars = self.make_vars() |
405 |
+ self.rrd_archives = self.make_rra() |
406 |
+ self.step = step if step is not None else self.__class__.STEP |
407 |
+ super ( StatsDB, self ).__init__ ( filepath ) |
408 |
+ # --- end of __init__ (...) --- |
409 |
+ |
410 |
+ def _do_create ( self, filepath ): |
411 |
+ return self._call_rrdtool ( |
412 |
+ ( |
413 |
+ 'create', filepath, |
414 |
+ '--start', str ( self.INIT_TIME ), |
415 |
+ '--step', str ( self.step ), |
416 |
+ ) + tuple ( |
417 |
+ v.get_key() for v in self.rrd_vars |
418 |
+ ) + tuple ( |
419 |
+ v.get_key() for v in self.rrd_archives |
420 |
+ ) |
421 |
+ ) |
422 |
+ # --- end of _do_create (...) --- |
423 |
+ |
424 |
+ def update ( self ): |
425 |
+ self.add ( self.collector.get_all() ) |
426 |
+ # --- end of update (...) --- |
427 |
+ |
428 |
+ def make_vars ( self ): |
429 |
+ return tuple ( |
430 |
+ RRDVariable ( k, 'DERIVE', val_max=0 ) |
431 |
+ for k in self.collector.NUMSTATS_KEYS |
432 |
+ ) |
433 |
+ # --- end of make_vars (...) --- |
434 |
+ |
435 |
+ def make_rra ( self ): |
436 |
+ return ( |
437 |
+ RRDArchive.new_day ( 'LAST', 0.7 ), |
438 |
+ RRDArchive.new_week ( 'AVERAGE', 0.7 ), |
439 |
+ RRDArchive.new_month ( 'AVERAGE', 0.7 ), |
440 |
+ ) |
441 |
+ # --- end of make_rra (...) --- |
442 |
+ |
443 |
+# --- end of StatsDB --- |
444 |
|
445 |
diff --git a/roverlay/stats/collector.py b/roverlay/stats/visualize.py |
446 |
similarity index 61% |
447 |
copy from roverlay/stats/collector.py |
448 |
copy to roverlay/stats/visualize.py |
449 |
index 3e3dee7..09ca48e 100644 |
450 |
--- a/roverlay/stats/collector.py |
451 |
+++ b/roverlay/stats/visualize.py |
452 |
@@ -1,4 +1,4 @@ |
453 |
-# R overlay -- stats collection, stats collector |
454 |
+# R overlay -- stats collection, print stats |
455 |
# -*- coding: utf-8 -*- |
456 |
# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
457 |
# Distributed under the terms of the GNU General Public License; |
458 |
@@ -6,84 +6,31 @@ |
459 |
|
460 |
import collections |
461 |
|
462 |
-from . import abstract |
463 |
-from . import base |
464 |
+import roverlay.util.objects |
465 |
|
466 |
|
467 |
-class StatsCollector ( abstract.RoverlayStatsBase ): |
468 |
+class StatsVisualizer ( object ): |
469 |
|
470 |
- _instance = None |
471 |
+ def __init__ ( self, stats ): |
472 |
+ super ( StatsVisualizer, self ).__init__() |
473 |
+ self.stats = stats |
474 |
+ self.lines = None |
475 |
|
476 |
- _MEMBERS = ( 'time', 'repo', 'distmap', 'overlay_creation', 'overlay', ) |
477 |
- |
478 |
- @classmethod |
479 |
- def get_instance ( cls ): |
480 |
- return cls._instance |
481 |
- # --- end of instance (...) --- |
482 |
- |
483 |
- def get_success_ratio ( self ): |
484 |
- # success ratio for "this" run: |
485 |
- # new ebuilds / relevant package count (new packages - unsuitable, |
486 |
- # where unsuitable is e.g. "OS_Type not supported") |
487 |
- # |
488 |
- return abstract.SuccessRatio ( |
489 |
- num_ebuilds = self.overlay_creation.pkg_success, |
490 |
- num_pkg = self.overlay_creation.get_relevant_package_count(), |
491 |
- ) |
492 |
- # --- end of get_success_ratio (...) --- |
493 |
- |
494 |
- def get_overall_success_ratio ( self ): |
495 |
- # overall success ratio: |
496 |
- # "relevant ebuild count" / "guessed package count" |
497 |
- # ratio := <all ebuilds> / (<all ebuilds> + <failed packages>) |
498 |
- # |
499 |
- # |
500 |
- # *Not* accurate as it includes imported ebuilds |
501 |
- # (Still better than using the repo package count since that may |
502 |
- # not include old package files) |
503 |
- # |
504 |
- return abstract.SuccessRatio ( |
505 |
- num_ebuilds = self.overlay.ebuild_count, |
506 |
- num_pkg = ( |
507 |
- self.overlay.ebuild_count + self.overlay_creation.pkg_fail |
508 |
- ), |
509 |
- ) |
510 |
- # --- end of get_overall_success_ratio (...) --- |
511 |
- |
512 |
- def get_net_gain ( self ): |
513 |
- return ( |
514 |
- self.overlay.ebuild_count - self.overlay.ebuilds_scanned |
515 |
- ) |
516 |
- # --- end of get_net_gain (...) --- |
517 |
- |
518 |
- def __init__ ( self ): |
519 |
- self.time = abstract.TimeStats ( "misc time stats" ) |
520 |
- self.distmap = base.DistmapStats() |
521 |
- self.overlay = base.OverlayStats() |
522 |
- self.overlay_creation = base.OverlayCreationStats() |
523 |
- self.repo = base.RepoStats() |
524 |
+ self.prepare() |
525 |
# --- end of __init__ (...) --- |
526 |
|
527 |
- def gen_str ( self ): |
528 |
- yield "{success}, overall {osuccess}".format ( |
529 |
- success = self.get_success_ratio(), |
530 |
- osuccess = self.get_overall_success_ratio(), |
531 |
- ) |
532 |
- yield "" |
533 |
- |
534 |
- for s in super ( StatsCollector, self ).gen_str(): |
535 |
- yield s |
536 |
- yield "" |
537 |
- # --- end of gen_str (...) --- |
538 |
- |
539 |
- def get_creation_str ( self ): |
540 |
- return str ( CreationStatsVisualizer ( self ) ) |
541 |
- # --- end of to_creation_str (...) --- |
542 |
+ @roverlay.util.objects.not_implemented |
543 |
+ def prepare ( self ): |
544 |
+ pass |
545 |
+ # --- end of prepare (...) --- |
546 |
|
547 |
-# --- end of StatsCollector --- |
548 |
+ def __str__ ( self ): |
549 |
+ return '\n'.join ( self.lines ) |
550 |
+ # --- end of __str__ (...) --- |
551 |
|
552 |
+# --- end of StatsVisualizer --- |
553 |
|
554 |
-class CreationStatsVisualizer ( abstract.StatsVisualizer ): |
555 |
+class CreationStatsVisualizer ( StatsVisualizer ): |
556 |
|
557 |
def prepare ( self ): |
558 |
EMPTY_LINE = "" |
559 |
@@ -206,7 +153,5 @@ class CreationStatsVisualizer ( abstract.StatsVisualizer ): |
560 |
|
561 |
self.lines = lines |
562 |
# --- end of gen_str (...) --- |
563 |
-# --- end of CreationStatsVisualizer --- |
564 |
|
565 |
-static = StatsCollector() |
566 |
-StatsCollector._instance = static |
567 |
+# --- end of CreationStatsVisualizer --- |