1"""A simple timer class to keep record of the elapsed time."""
2
3import time
4
5
6class TimeRecorder(object):
7    """Main class to keep time records.
8
9    A timer record contains an ID, a start timestamp, and an optional stop
10    timestamps. The elapsed time calculated as stop - start.
11    If the stop timestamp is not set, current system time will be used.
12
13    Example usage:
14    >>> timer = TimeRecorder()
15    >>> # start a single timer, ID = 'lunch'
16    >>> timer.start_timer('lunch')
17    >>> # start two timers at the same time
18    >>> timer.start_timer(['salad', 'dessert'])
19    >>> # stop a single timer
20    >>> timer.stop_timer('salad')
21    >>> # get elapsed time of all timers
22    >>> timer.elapsed()
23    """
24
25    def __init__(self):
26        self.recorder = dict()
27
28    def start_timer(self, record_ids='Default', force=False):
29        """Start one or more timer.
30
31        Starts one or more timer at current system time with the record ID
32        specified in record_ids. Will overwrite/restart existing timer.
33
34        Args:
35            record_ids: timer record IDs. Can be a string or a list of strings.
36                        If the record ID is a list, will start multiple timers
37                        at the same time.
38            force: Force update the timer's start time if the specified timer
39                   has already started. By default we won't update started timer
40                   again.
41
42        Returns:
43            Number of timer started.
44        """
45        if isinstance(record_ids, str):
46            record_ids = [record_ids]
47        start_time = time.time()
48        for rec in record_ids:
49            if force or rec not in self.recorder:
50                self.recorder[rec] = [start_time, None]
51        return len(record_ids)
52
53    def stop_timer(self, record_ids=None, force=False):
54        """Stop one or more timer.
55
56        Stops one or more timer at current system time.
57
58        Args:
59            record_ids: timer record IDs. Can be a string or a list of strings.
60                        If the record ID is a list, will stop multiple timers at
61                        the same time. By default, it will stop all timers.
62            force: Force update the timer's stop time if the specified timer has
63                   already stopped. By default we won't update stopped timer
64                   again.
65
66        Returns:
67            Number of timer stopped.
68        """
69        # stop all record if id is not provided.
70        if record_ids is None:
71            record_ids = self.recorder.keys()
72        elif isinstance(record_ids, str):
73            record_ids = [record_ids]
74        stop_time = time.time()
75        num_rec = 0
76        for rec in record_ids:
77            if rec in self.recorder:
78                if force or self.recorder[rec][1] is None:
79                    self.recorder[rec][1] = stop_time
80                    num_rec += 1
81        return num_rec
82
83    def elapsed(self, record_ids=None):
84        """Return elapsed time in seconds.
85
86        For records with no stop time, will calculate based on the current
87        system time.
88
89        Args:
90            record_ids: timer record IDs. Can be a string or a list of strings.
91                        If the record ID is a list, will compute the elapsed
92                        time for all specified timers. Default value (None)
93                        calculates elapsed time for all existing timers.
94
95        Returns:
96            The elapsed time. If the record_ids is a string, will return the
97            time in seconds as float type. If the record_ids is a list or
98            default (None), will return a dict of the <record id, elapsed time>.
99        """
100        single_record = False
101        if record_ids is None:
102            record_ids = self.recorder.keys()
103        elif isinstance(record_ids, str):
104            record_ids = [record_ids]
105            single_record = True
106        results = dict()
107        curr_time = time.time()
108        for rec in record_ids:
109            if rec in self.recorder:
110                if self.recorder[rec][1] is not None:
111                    results[rec] = self.recorder[rec][1] - self.recorder[rec][0]
112                else:
113                    results[rec] = curr_time - self.recorder[rec][0]
114        if not results:  # no valid record found
115            return None
116        elif single_record and len(record_ids) == 1:
117            # only 1 record is requested, return results directly
118            return results[record_ids[0]]
119        else:
120            return results  # multiple records, return a dict.
121
122    def clear(self, record_ids=None):
123        """Clear existing time records."""
124        if record_ids is None:
125            self.recorder = dict()
126            return
127
128        if isinstance(record_ids, str):
129            record_ids = [record_ids]
130        for rec in record_ids:
131            if rec in self.recorder:
132                del self.recorder[rec]
133