# -*- coding: utf-8 -*- # # Copyright 2011 Google Inc. All Rights Reserved. # """Tools for recording and reporting timeline of abstract events. You can store any events provided that they can be stringified. """ __author__ = 'kbaclawski@google.com (Krystian Baclawski)' import collections import datetime import time class _EventRecord(object): """Internal class. Attaches extra information to an event.""" def __init__(self, event, time_started=None, time_elapsed=None): self._event = event self._time_started = time_started or time.time() self._time_elapsed = None if time_elapsed: self.time_elapsed = time_elapsed @property def event(self): return self._event @property def time_started(self): return self._time_started def _TimeElapsedGet(self): if self.has_finished: time_elapsed = self._time_elapsed else: time_elapsed = time.time() - self._time_started return datetime.timedelta(seconds=time_elapsed) def _TimeElapsedSet(self, time_elapsed): if isinstance(time_elapsed, datetime.timedelta): self._time_elapsed = time_elapsed.seconds else: self._time_elapsed = time_elapsed time_elapsed = property(_TimeElapsedGet, _TimeElapsedSet) @property def has_finished(self): return self._time_elapsed is not None def GetTimeStartedFormatted(self): return time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(self._time_started)) def GetTimeElapsedRounded(self): return datetime.timedelta(seconds=int(self.time_elapsed.seconds)) def Finish(self): if not self.has_finished: self._time_elapsed = time.time() - self._time_started class _Transition(collections.namedtuple('_Transition', ('from_', 'to_'))): """Internal class. Represents transition point between events / states.""" def __str__(self): return '%s => %s' % (self.from_, self.to_) class EventHistory(collections.Sequence): """Records events and provides human readable events timeline.""" def __init__(self, records=None): self._records = records or [] def __len__(self): return len(self._records) def __iter__(self): return iter(self._records) def __getitem__(self, index): return self._records[index] @property def last(self): if self._records: return self._records[-1] def AddEvent(self, event): if self.last: self.last.Finish() evrec = _EventRecord(event) self._records.append(evrec) return evrec def GetTotalTime(self): if self._records: total_time_elapsed = sum(evrec.time_elapsed.seconds for evrec in self._records) return datetime.timedelta(seconds=int(total_time_elapsed)) def GetTransitionEventHistory(self): records = [] if self._records: for num, next_evrec in enumerate(self._records[1:], start=1): evrec = self._records[num - 1] records.append(_EventRecord( _Transition(evrec.event, next_evrec.event), evrec.time_started, evrec.time_elapsed)) if not self.last.has_finished: records.append(_EventRecord( _Transition(self.last.event, 'NOW'), self.last.time_started, self.last.time_elapsed)) return EventHistory(records) @staticmethod def _GetReport(history, report_name): report = [report_name] for num, evrec in enumerate(history, start=1): time_elapsed = str(evrec.GetTimeElapsedRounded()) if not evrec.has_finished: time_elapsed.append(' (not finished)') report.append('%d) %s: %s: %s' % (num, evrec.GetTimeStartedFormatted(), evrec.event, time_elapsed)) report.append('Total Time: %s' % history.GetTotalTime()) return '\n'.join(report) def GetEventReport(self): return EventHistory._GetReport(self, 'Timeline of events:') def GetTransitionEventReport(self): return EventHistory._GetReport(self.GetTransitionEventHistory(), 'Timeline of transition events:')