naeural-client 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. naeural_client/__init__.py +13 -0
  2. naeural_client/_ver.py +13 -0
  3. naeural_client/base/__init__.py +6 -0
  4. naeural_client/base/distributed_custom_code_presets.py +44 -0
  5. naeural_client/base/generic_session.py +1763 -0
  6. naeural_client/base/instance.py +616 -0
  7. naeural_client/base/payload/__init__.py +1 -0
  8. naeural_client/base/payload/payload.py +66 -0
  9. naeural_client/base/pipeline.py +1499 -0
  10. naeural_client/base/plugin_template.py +5209 -0
  11. naeural_client/base/responses.py +209 -0
  12. naeural_client/base/transaction.py +157 -0
  13. naeural_client/base_decentra_object.py +143 -0
  14. naeural_client/bc/__init__.py +3 -0
  15. naeural_client/bc/base.py +1046 -0
  16. naeural_client/bc/chain.py +0 -0
  17. naeural_client/bc/ec.py +324 -0
  18. naeural_client/certs/__init__.py +0 -0
  19. naeural_client/certs/r9092118.ala.eu-central-1.emqxsl.com.crt +22 -0
  20. naeural_client/code_cheker/__init__.py +1 -0
  21. naeural_client/code_cheker/base.py +520 -0
  22. naeural_client/code_cheker/checker.py +294 -0
  23. naeural_client/comm/__init__.py +2 -0
  24. naeural_client/comm/amqp_wrapper.py +338 -0
  25. naeural_client/comm/mqtt_wrapper.py +539 -0
  26. naeural_client/const/README.md +3 -0
  27. naeural_client/const/__init__.py +9 -0
  28. naeural_client/const/base.py +101 -0
  29. naeural_client/const/comms.py +80 -0
  30. naeural_client/const/environment.py +26 -0
  31. naeural_client/const/formatter.py +7 -0
  32. naeural_client/const/heartbeat.py +111 -0
  33. naeural_client/const/misc.py +20 -0
  34. naeural_client/const/payload.py +190 -0
  35. naeural_client/default/__init__.py +1 -0
  36. naeural_client/default/instance/__init__.py +4 -0
  37. naeural_client/default/instance/chain_dist_custom_job_01_plugin.py +54 -0
  38. naeural_client/default/instance/custom_web_app_01_plugin.py +118 -0
  39. naeural_client/default/instance/net_mon_01_plugin.py +45 -0
  40. naeural_client/default/instance/view_scene_01_plugin.py +28 -0
  41. naeural_client/default/session/mqtt_session.py +72 -0
  42. naeural_client/io_formatter/__init__.py +2 -0
  43. naeural_client/io_formatter/base/__init__.py +1 -0
  44. naeural_client/io_formatter/base/base_formatter.py +80 -0
  45. naeural_client/io_formatter/default/__init__.py +3 -0
  46. naeural_client/io_formatter/default/a_dummy.py +51 -0
  47. naeural_client/io_formatter/default/aixp1.py +113 -0
  48. naeural_client/io_formatter/default/default.py +22 -0
  49. naeural_client/io_formatter/io_formatter_manager.py +96 -0
  50. naeural_client/logging/__init__.py +1 -0
  51. naeural_client/logging/base_logger.py +2056 -0
  52. naeural_client/logging/logger_mixins/__init__.py +12 -0
  53. naeural_client/logging/logger_mixins/class_instance_mixin.py +92 -0
  54. naeural_client/logging/logger_mixins/computer_vision_mixin.py +443 -0
  55. naeural_client/logging/logger_mixins/datetime_mixin.py +344 -0
  56. naeural_client/logging/logger_mixins/download_mixin.py +421 -0
  57. naeural_client/logging/logger_mixins/general_serialization_mixin.py +242 -0
  58. naeural_client/logging/logger_mixins/json_serialization_mixin.py +481 -0
  59. naeural_client/logging/logger_mixins/pickle_serialization_mixin.py +301 -0
  60. naeural_client/logging/logger_mixins/process_mixin.py +63 -0
  61. naeural_client/logging/logger_mixins/resource_size_mixin.py +81 -0
  62. naeural_client/logging/logger_mixins/timers_mixin.py +501 -0
  63. naeural_client/logging/logger_mixins/upload_mixin.py +260 -0
  64. naeural_client/logging/logger_mixins/utils_mixin.py +675 -0
  65. naeural_client/logging/small_logger.py +93 -0
  66. naeural_client/logging/tzlocal/__init__.py +20 -0
  67. naeural_client/logging/tzlocal/unix.py +231 -0
  68. naeural_client/logging/tzlocal/utils.py +113 -0
  69. naeural_client/logging/tzlocal/win32.py +151 -0
  70. naeural_client/logging/tzlocal/windows_tz.py +718 -0
  71. naeural_client/plugins_manager_mixin.py +273 -0
  72. naeural_client/utils/__init__.py +2 -0
  73. naeural_client/utils/comm_utils.py +44 -0
  74. naeural_client/utils/dotenv.py +75 -0
  75. naeural_client-2.0.0.dist-info/METADATA +365 -0
  76. naeural_client-2.0.0.dist-info/RECORD +78 -0
  77. naeural_client-2.0.0.dist-info/WHEEL +4 -0
  78. naeural_client-2.0.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,501 @@
1
+ import traceback
2
+ import numpy as np
3
+
4
+ from collections import OrderedDict, deque
5
+ from time import perf_counter, sleep, time
6
+
7
+
8
+ DEFAULT_SECTION = 'main'
9
+ DEFAULT_THRESHOLD_NO_SHOW = 0
10
+ PERIODS = [60 * 60, 24 * 60 * 60, 30 * 24 * 60 * 60]
11
+ PERIODS_STR = ["hour", "day", "month"]
12
+ MAX_PERIOD_LAPS = 20
13
+ DEFAULT_PERIODIC_MULTIPLIER = 3
14
+
15
+ MAX_LAPS = 100
16
+ ZERO_THRESHOLD = 5e-4
17
+
18
+ _OBSOLETE_SECTION_TIME = 3600 # sections older than 1 hour are archived
19
+
20
+ class _TimersMixin(object):
21
+ """
22
+ Mixin for timers functionalities that are attached to `pye2.Logger`.
23
+
24
+ This mixin cannot be instantiated because it is built just to provide some additional
25
+ functionalities for `pye2.Logger`
26
+
27
+ In this mixin we can use any attribute/method of the Logger.
28
+ """
29
+
30
+ def __init__(self):
31
+ super(_TimersMixin, self).__init__()
32
+ self.timers = None
33
+ self.timer_level = None # no actual purpose following addition of graph
34
+ self.sections_last_used = {}
35
+ self.opened_timers = None
36
+ self.timers_graph = None
37
+ self._timer_error = None
38
+ self.default_timers_section = DEFAULT_SECTION
39
+ self.__timer_mutex = False
40
+
41
+ self.start_show_timer = None
42
+
43
+ self.reset_timers()
44
+ return
45
+
46
+ def _maybe_create_timers_section(self, section=None):
47
+ section = section or self.default_timers_section
48
+
49
+ if section in self.timers:
50
+ return
51
+
52
+ self.timers[section] = OrderedDict()
53
+ self.timer_level[section] = 0
54
+ self.opened_timers[section] = deque()
55
+ self.timers_graph[section] = OrderedDict()
56
+ self.timers_graph[section]["ROOT"] = {"SLOW" : OrderedDict(), "FAST" : OrderedDict()}
57
+ self._timer_error[section] = False
58
+ return
59
+
60
+ def reset_timers(self):
61
+ self.timers = {}
62
+ self.timer_level = {}
63
+ self.opened_timers = {}
64
+ self.timers_graph = {}
65
+ self._timer_error = {}
66
+
67
+ self._maybe_create_timers_section()
68
+ return
69
+
70
+ @staticmethod
71
+ def get_empty_timer():
72
+ return {
73
+ 'MEAN': 0,
74
+ 'MAX': 0,
75
+ 'COUNT': 0,
76
+ 'START': 0,
77
+ 'END': 0,
78
+ 'PASS': True,
79
+ 'LEVEL': 0,
80
+
81
+ 'START_COUNT': 0,
82
+ 'STOP_COUNT': 0,
83
+
84
+ 'LAPS' : deque(maxlen=MAX_LAPS),
85
+ 'HISTORY': {
86
+ 'LAST': [None for _ in range(len(PERIODS))],
87
+ 'DEQUES': [deque(maxlen=MAX_PERIOD_LAPS) for _ in range(len(PERIODS))],
88
+ }
89
+ }
90
+
91
+ def restart_timer(self, sname, section=None):
92
+ section = section or self.default_timers_section
93
+ self.timers[section][sname] = self.get_empty_timer()
94
+ return
95
+
96
+ def _add_in_timers_graph(self, sname, section=None):
97
+ section = section or self.default_timers_section
98
+ self.timers[section][sname]['LEVEL'] = self.timer_level[section]
99
+ if sname not in self.timers_graph[section]:
100
+ self.timers_graph[section][sname] = {"SLOW" : OrderedDict(), "FAST" : OrderedDict()}
101
+
102
+ self.timers_graph[section][sname]["FAST"] = OrderedDict() ## there is no ordered set, so we use OrderedDict with no values
103
+ return
104
+
105
+ def start_timer(self, sname, section=None):
106
+ section = section or self.default_timers_section
107
+ if section == self.default_timers_section:
108
+ assert self.is_main_thread, "Attempted to run threaded timer '{}' without section".format(sname)
109
+
110
+ self._maybe_create_timers_section(section)
111
+
112
+ if not self.DEBUG:
113
+ return -1
114
+
115
+ if sname not in self.timers[section]:
116
+ self.restart_timer(sname, section)
117
+
118
+ curr_time = perf_counter()
119
+ self._add_in_timers_graph(sname, section=section)
120
+ self.timers[section][sname]['START'] = curr_time
121
+ self.timers[section][sname]['START_COUNT'] += 1
122
+ if len(self.opened_timers[section]) >= 1:
123
+ parent = self.opened_timers[section][-1]
124
+ else:
125
+ parent = "ROOT"
126
+ #endif
127
+
128
+ self.timers_graph[section][parent]["FAST"][sname] = None
129
+ self.timers_graph[section][parent]["SLOW"][sname] = None
130
+
131
+ self.timer_level[section] += 1
132
+ self.opened_timers[section].append(sname)
133
+
134
+ faulty_timers = self._get_section_faulty_timers(section)
135
+ if len(faulty_timers) > 0 and not self._timer_error[section]:
136
+ self.P("Something is wrong with the timers in section '{}':".format(section), color='r')
137
+ for ft in faulty_timers:
138
+ self.P(" {}: {}".format(ft, self.timers[section][ft]), color='r')
139
+ self._timer_error[section] = True
140
+ #endif
141
+
142
+ return curr_time
143
+
144
+ def get_time_until_now(self, sname, section=None):
145
+ section = section or self.default_timers_section
146
+ ctimer = self.timers[section][sname]
147
+ return perf_counter() - ctimer['START']
148
+
149
+ def get_faulty_timers(self):
150
+ dct_faulty = {}
151
+ for section in self.timers:
152
+ dct_faulty[section] = self._get_section_faulty_timers(section)
153
+ return dct_faulty
154
+
155
+ def _get_section_faulty_timers(self, section=None):
156
+ section = section or self.default_timers_section
157
+ lst_faulty = []
158
+ for tmr_name, tmr in self.timers[section].items():
159
+ if (tmr['START_COUNT'] - tmr['STOP_COUNT']) > 1:
160
+ lst_faulty.append(tmr_name)
161
+ return lst_faulty
162
+
163
+ def end_timer_no_skip(self, sname, section=None, periodic=False):
164
+ return self.end_timer(sname, skip_first_timing=False, section=section, periodic=periodic)
165
+
166
+ def get_opened_timer(self, section=None):
167
+ section = section or self.default_timers_section
168
+ timer = self.opened_timers[section][-1]
169
+ return timer, perf_counter() - self.timers[section][timer]['START']
170
+
171
+ def get_periodic_multiplier(self):
172
+ if self.config_data is None:
173
+ return DEFAULT_PERIODIC_MULTIPLIER
174
+ return self.config_data.get('PERIODIC_MULTIPLIER', DEFAULT_PERIODIC_MULTIPLIER)
175
+
176
+ def add_periodic_record(self, sname, record, section=None, idx=0, check=False):
177
+ chistory = self.timers[section][sname]['HISTORY']
178
+ dq_size = len(chistory['DEQUES'][idx])
179
+ if check and dq_size > 2:
180
+ cmean = np.array(chistory['DEQUES'][idx]).mean()
181
+ cstd = np.std(chistory['DEQUES'][idx])
182
+ cnt = self.timers[section][sname]['COUNT']
183
+ q = self.get_periodic_multiplier()
184
+ limit = cmean + cstd * q
185
+ if limit < record:
186
+ self.P(
187
+ f"[WARNING]Timer {'' if section is None else section + '|'}{sname} "
188
+ f"took longer than expected at iter {cnt} based on {dq_size} records ("
189
+ f"{round(record, 3)} > {round(limit, 3)}[{round(cmean, 3)}+{round(q, 3)}*{round(cstd, 3)}]) [{PERIODS_STR[idx]}]",
190
+ color='r'
191
+ )
192
+ # endif warning
193
+ # endif anomaly check
194
+ chistory['DEQUES'][idx].append(record)
195
+ chistory['LAST'][idx] = self.timers[section][sname]['END']
196
+ return
197
+
198
+ def end_timer(self, sname, skip_first_timing=False, section=None, periodic=False):
199
+ section = section or self.default_timers_section
200
+ if sname not in self.timers[section]:
201
+ return
202
+ result = 0
203
+ self.sections_last_used[section] = time()
204
+ if self.DEBUG:
205
+ self.opened_timers[section].pop()
206
+ self.timer_level[section] -= 1
207
+
208
+ ctimer = self.timers[section][sname]
209
+ ctimer['STOP_COUNT'] += 1
210
+ ctimer['END'] = perf_counter()
211
+ result = ctimer['END'] - ctimer['START']
212
+ ctimer['LAPS'].append(result)
213
+ if periodic:
214
+ chistory = ctimer['HISTORY']
215
+ for i, period in enumerate(PERIODS):
216
+ if chistory['LAST'][i] is None:
217
+ self.add_periodic_record(sname, result, section=section, idx=i)
218
+ elif ctimer['END'] - chistory['LAST'][i] > period:
219
+ self.add_periodic_record(sname, result, section=section, idx=i, check=True)
220
+ # endif periodic record
221
+ # endfor i, period
222
+ # endif periodic
223
+ _count = ctimer['COUNT']
224
+ _prev_avg = ctimer['MEAN']
225
+ avg = _count * _prev_avg
226
+
227
+ if ctimer['PASS'] and skip_first_timing:
228
+ ctimer['PASS'] = False
229
+ return result # do not record first timing in average nor the max
230
+
231
+ ctimer['MAX'] = max(ctimer['MAX'], result)
232
+
233
+ ctimer['COUNT'] = _count + 1
234
+ avg += result
235
+ avg = avg / ctimer["COUNT"]
236
+ ctimer['MEAN'] = avg
237
+ return result
238
+
239
+ def stop_timer(self, sname, skip_first_timing=False, section=None, periodic=False):
240
+ return self.end_timer(sname=sname, skip_first_timing=skip_first_timing, section=section, periodic=periodic)
241
+
242
+ def show_timer_total(self, sname, section=None):
243
+ section = section or self.default_timers_section
244
+ ctimer = self.timers[section][sname]
245
+ cnt = ctimer['COUNT']
246
+ val = ctimer['MEAN'] * cnt
247
+ self.P(" {} = {:.3f} in {} laps".format(sname, val, cnt))
248
+ return
249
+
250
+ def _format_timer(self, key, section, was_recently_seen,
251
+ summary='mean',
252
+ show_levels=True,
253
+ show_max=True,
254
+ show_last=True,
255
+ show_count=True,
256
+ div=None,
257
+ threshold_no_show=None,
258
+ max_key_size=30,
259
+ ):
260
+
261
+ if threshold_no_show is None:
262
+ threshold_no_show = DEFAULT_THRESHOLD_NO_SHOW
263
+
264
+ ctimer = self.timers[section].get(key, None)
265
+
266
+ if ctimer is None:
267
+ return
268
+
269
+ mean_time = ctimer['MEAN']
270
+ np_laps = np.array(ctimer['LAPS'])
271
+ if len(np_laps) > 0:
272
+ laps_mean = np_laps.mean()
273
+ laps_std = np_laps.std()
274
+ laps_zcount = (np_laps <= ZERO_THRESHOLD).sum()
275
+ laps_nzcount = len(np_laps) - laps_zcount
276
+ laps_nz_mean = np_laps.sum() / laps_nzcount if laps_nzcount > 0 else -1
277
+ laps_low_cnt = (np_laps <= max(ZERO_THRESHOLD, laps_mean - laps_std)).sum()
278
+ laps_low_prc = laps_low_cnt / np_laps.shape[0] * 100
279
+ else:
280
+ # not mandatory but nice for forever opened 1 time timers
281
+ laps_mean = -1
282
+ laps_std = -1
283
+ laps_zcount = -1
284
+ laps_nzcount = -1
285
+ laps_nz_mean = -1
286
+ laps_low_cnt = -1
287
+ laps_low_prc = -1
288
+ # end not mandatory
289
+
290
+ count = ctimer['COUNT']
291
+
292
+ if mean_time < threshold_no_show:
293
+ return
294
+
295
+ max_time = ctimer['MAX']
296
+ current_time = np_laps[-1] if len(np_laps) > 0 else -1 # ctimer['END'] - ctimer['START']
297
+
298
+ if not was_recently_seen:
299
+ key = '[' + key[:max_key_size] + ']'
300
+
301
+ if show_levels:
302
+ s_key = ' ' * ctimer['LEVEL'] + key[:max_key_size]
303
+ else:
304
+ s_key = key[:max_key_size]
305
+ msg = None
306
+ if summary in ['mean', 'avg']:
307
+ # self.verbose_log(" {} = {:.4f}s (max lap = {:.4f}s)".format(s_key,mean_time,max_time))
308
+ msg = " {} = {:.4f}s/q:{:.4f}s/nz:{:.4f}s".format(s_key, mean_time, laps_mean, laps_nz_mean)
309
+ else:
310
+ # self.verbose_log(" {} = {:.4f}s (max lap = {:.4f}s)".format(s_key,total, max_time))
311
+ total = mean_time * count
312
+ msg = " {} = {:.4f}s".format(s_key, total)
313
+ if show_max:
314
+ msg += ", max: {:.4f}s".format(max_time)
315
+ if show_last:
316
+ msg += ", lst: {:.4f}s".format(current_time)
317
+ if show_count:
318
+ msg += ", c: {}/L:{:.0f}%".format(count, laps_low_prc)
319
+ if div is not None:
320
+ msg += ", itr(B{}): {:.4f}s".format(div, mean_time / div)
321
+ return msg
322
+
323
+ def show_timers(self, indent=4, **kwargs):
324
+ color = kwargs.pop('color', 'n')
325
+ lst_logs = self.format_timers(**kwargs)
326
+ if indent > 0:
327
+ lst_logs = [" " * indent + l for l in lst_logs]
328
+ if len(lst_logs) > 0:
329
+ full_log = "\n".join(lst_logs)
330
+ self.verbose_log(full_log.lstrip(), color=color)
331
+ return
332
+
333
+ def show_timings(self, **kwargs):
334
+ self.show_timers(**kwargs)
335
+ return
336
+
337
+ def format_timers(self, summary=None,
338
+ title=None,
339
+ show_levels=True,
340
+ show_max=True,
341
+ show_last=True,
342
+ show_count=True,
343
+ div=None,
344
+ threshold_no_show=None,
345
+ selected_sections=None,
346
+ obsolete_section_time=_OBSOLETE_SECTION_TIME,
347
+ ):
348
+
349
+ if selected_sections is not None:
350
+ assert isinstance(selected_sections, list)
351
+
352
+ if threshold_no_show is None:
353
+ threshold_no_show = DEFAULT_THRESHOLD_NO_SHOW
354
+
355
+ if summary is None:
356
+ summary = 'mean'
357
+
358
+ if title is None:
359
+ title = ''
360
+
361
+ lst_logs = []
362
+ self.start_show_timer = time()
363
+ try:
364
+ while self.__timer_mutex:
365
+ elapsed_in_show = time() - self.start_show_timer
366
+ if elapsed_in_show > 15:
367
+ raise ValueError("show_timers: Run time exceeded! Mutex is locked for more than 15 seconds.")
368
+ sleep(0.001)
369
+ # end while
370
+ self.__timer_mutex = True
371
+
372
+ self.start_show_timer = time()
373
+ self.__dfs_stack = deque(maxlen=100)
374
+
375
+ def dfs(visited, graph, node, was_recently_seen, logs, sect):
376
+ self.__dfs_stack.append(node)
377
+ elapsed_in_show = time() - self.start_show_timer
378
+
379
+ if elapsed_in_show > 5:
380
+ raise ValueError("show_timers: Run time exceeded! DFS took longer than 5 seconds. Stack: {}".format(self.__dfs_stack))
381
+
382
+ if node not in visited:
383
+ formatted_node = self._format_timer(
384
+ key=node,
385
+ section=sect,
386
+ was_recently_seen=was_recently_seen,
387
+ summary=summary,
388
+ show_levels=show_levels, show_last=show_last,
389
+ show_max=show_max, show_count=show_count, div=div,
390
+ threshold_no_show=threshold_no_show
391
+ )
392
+ if formatted_node is not None:
393
+ logs.append(formatted_node)
394
+ visited.add(node)
395
+ slow_keys = list(graph[node]["SLOW"].keys())
396
+ fast_keys = list(graph[node]["FAST"].keys())
397
+ for neighbour in slow_keys:
398
+ recently_seen = neighbour in fast_keys
399
+ dfs(visited, graph, neighbour, recently_seen, logs, sect)
400
+ #endfor
401
+ #endif
402
+ #enddef
403
+
404
+ if self.DEBUG:
405
+ if len(title) > 0:
406
+ title = ' ' + title
407
+ header = "Timing results{} at {}:".format(title, self.now_str(nice_print=True, short=True))
408
+ if threshold_no_show > 0:
409
+ header += " (discarding entries with time < {})".format(threshold_no_show)
410
+ lst_logs.append(header)
411
+
412
+ ## SORTING sections and keeping the default section the first one ..
413
+ keys = list(self.timers.keys())
414
+ if selected_sections is not None:
415
+ keys = selected_sections
416
+
417
+ add_back_default_section = False
418
+ if self.default_timers_section in keys:
419
+ add_back_default_section = True
420
+ keys.pop(keys.index(self.default_timers_section))
421
+ keys.sort()
422
+ if add_back_default_section:
423
+ keys = [self.default_timers_section] + keys
424
+
425
+ old_sections = []
426
+ for section in keys:
427
+ last_see_ago = None
428
+ if section in self.sections_last_used:
429
+ last_see_ago = time() - self.sections_last_used[section]
430
+ if last_see_ago > obsolete_section_time:
431
+ old_sections.append(section)
432
+ continue
433
+ lst_logs.append("Section '{}'{}".format(
434
+ section, " last seen {:.1f}s ago".format(last_see_ago) if last_see_ago is not None else ""
435
+ ))
436
+ buffer_visited = set()
437
+ dfs(buffer_visited, self.timers_graph[section], "ROOT", True, lst_logs, section)
438
+ if len(old_sections) > 0:
439
+ lst_logs.append("Archived {} sections older than {:.1f} hrs.".format(
440
+ len(old_sections), obsolete_section_time / 3600,
441
+ ))
442
+ else:
443
+ self.verbose_log("DEBUG not activated!")
444
+ except Exception as exc:
445
+ self.P("EXCEPTION in show_timers: {}. Stacktrace:\n{}".format(exc, traceback.format_exc()), color='r')
446
+ self.__timer_mutex = False
447
+ return lst_logs
448
+
449
+ def get_timing_dict(self, skey, section=None):
450
+ section = section or self.default_timers_section
451
+ timers_section = self.timers.get(section, {})
452
+ dct = timers_section.get(skey, {})
453
+ return dct
454
+
455
+ def get_timer(self, skey, section=None):
456
+ return self.get_timing_dict(skey, section=section)
457
+
458
+ def get_timer_overall_mean(self, skey, section=None):
459
+ tmr = self.get_timer(skey, section=section)
460
+ result = tmr.get('MEAN', 0)
461
+ return result
462
+
463
+ def get_timer_mean(self, skey, section=None):
464
+ tmr = self.get_timer(skey, section=section)
465
+ laps = tmr.get('LAPS', [])
466
+ result = np.mean(laps) if len(laps) > 0 else -1
467
+ return result
468
+
469
+
470
+ def get_timer_count(self, skey, section=None):
471
+ tmr = self.get_timer(skey, section=section)
472
+ result = tmr.get('COUNT', 0)
473
+ return result
474
+
475
+ def import_timers_section(self, dct_timers, dct_timers_graph, section, overwrite=False):
476
+ if self.__timer_mutex:
477
+ # we skip this import until next time
478
+ self.P("WARNING: Cannot import section '{}' with {} timers while processing sections!".format(
479
+ section, len(dct_timers)
480
+ ), color='r')
481
+ return
482
+ if section in self.timers and not overwrite:
483
+ self.P("WARNING: Cannot import section '{}' with {} timers as there is already a existing section".format(
484
+ section, len(dct_timers)
485
+ ), color='r')
486
+ return False
487
+ self.timers[section] = dct_timers
488
+ self.timers_graph[section] = dct_timers_graph
489
+ self.sections_last_used[section] = time()
490
+ return True
491
+
492
+ def export_timers_section(self, section=None):
493
+ section = section or self.default_timers_section
494
+ if section not in self.timers:
495
+ self.P("WARNING: Cannot export unexisting timers section '{}'".format(
496
+ section
497
+ ), color='r')
498
+ return None, None
499
+ dct_timers = self.timers[section]
500
+ dct_timers_graph = self.timers_graph[section]
501
+ return dct_timers, dct_timers_graph