Skip to content

Commit 44371e9

Browse files
authored
fix(record): fix performance plotting regression (#774)
* Fixes regressions introduced in Database access refactor #676 affecting plotting of performance plots. * Re-uses record.write_event for video events. * Modifies plot_performance to use color-blind-friendly markers * black/flake8
1 parent 835bb17 commit 44371e9

File tree

2 files changed

+58
-21
lines changed

2 files changed

+58
-21
lines changed

openadapt/plotting.py

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from collections import defaultdict
44
from io import BytesIO
5+
from itertools import cycle
56
import math
67
import os
78
import unicodedata
@@ -15,7 +16,7 @@
1516

1617
from openadapt.config import PERFORMANCE_PLOTS_DIR_PATH, config
1718
from openadapt.models import ActionEvent
18-
from openadapt import common, utils
19+
from openadapt import common, models, utils
1920

2021

2122
# TODO: move parameters to config
@@ -321,15 +322,15 @@ def display_event(
321322

322323

323324
def plot_performance(
324-
recording_timestamp: float = None,
325+
recording: models.Recording | None = None,
325326
view_file: bool = False,
326327
save_file: bool = True,
327328
dark_mode: bool = False,
328329
) -> str:
329330
"""Plot the performance of the event processing and writing.
330331
331332
Args:
332-
recording_timestamp: The timestamp of the recording (defaults to latest)
333+
recording: The Recording whose performance to plot (defaults to latest).
333334
view_file: Whether to view the file after saving it.
334335
save_file: Whether to save the file.
335336
dark_mode: Whether to use dark mode.
@@ -339,34 +340,56 @@ def plot_performance(
339340
"""
340341
type_to_proc_times = defaultdict(list)
341342
type_to_timestamps = defaultdict(list)
342-
event_types = set()
343343

344344
if dark_mode:
345345
plt.style.use("dark_background")
346346

347-
# avoid circular import
348347
from openadapt.db import crud
349348

350-
if not recording_timestamp:
351-
recording_timestamp = crud.get_latest_recording().timestamp
352-
perf_stats = crud.get_perf_stats(recording_timestamp)
349+
if not recording:
350+
recording = crud.get_latest_recording()
351+
session = crud.get_new_session(read_only=True)
352+
perf_stats = crud.get_perf_stats(session, recording)
353353
for perf_stat in perf_stats:
354354
event_type = perf_stat.event_type
355355
start_time = perf_stat.start_time
356356
end_time = perf_stat.end_time
357357
type_to_proc_times[event_type].append(end_time - start_time)
358-
event_types.add(event_type)
359358
type_to_timestamps[event_type].append(start_time)
360359

361360
fig, ax = plt.subplots(1, 1, figsize=(20, 10))
361+
362+
# Define markers to distinguish different event types
363+
markers = [
364+
"o",
365+
"s",
366+
"D",
367+
"^",
368+
"v",
369+
">",
370+
"<",
371+
"p",
372+
"*",
373+
"h",
374+
"H",
375+
"+",
376+
"x",
377+
"X",
378+
"d",
379+
"|",
380+
"_",
381+
]
382+
marker_cycle = cycle(markers)
383+
362384
for event_type in type_to_proc_times:
363385
x = type_to_timestamps[event_type]
364386
y = type_to_proc_times[event_type]
365-
ax.scatter(x, y, label=event_type)
387+
ax.scatter(x, y, label=event_type, marker=next(marker_cycle))
388+
366389
ax.legend()
367390
ax.set_ylabel("Duration (seconds)")
368391

369-
mem_stats = crud.get_memory_stats(recording_timestamp)
392+
mem_stats = crud.get_memory_stats(session, recording)
370393
timestamps = []
371394
mem_usages = []
372395
for mem_stat in mem_stats:
@@ -383,21 +406,18 @@ def plot_performance(
383406
memory_ax.set_ylabel("Memory Usage (bytes)")
384407

385408
if len(mem_usages) > 0:
386-
# Get the handles and labels from both axes
387409
handles1, labels1 = ax.get_legend_handles_labels()
388410
handles2, labels2 = memory_ax.get_legend_handles_labels()
389411

390-
# Combine the handles and labels from both axes
391412
all_handles = handles1 + handles2
392413
all_labels = labels1 + labels2
393414

394415
ax.legend(all_handles, all_labels)
395416

396-
ax.set_title(f"{recording_timestamp=}")
417+
ax.set_title(f"{recording.timestamp=}")
397418

398-
# TODO: add PROC_WRITE_BY_EVENT_TYPE
399419
if save_file:
400-
fname_parts = ["performance", str(recording_timestamp)]
420+
fname_parts = ["performance", str(recording.timestamp)]
401421
fname = "-".join(fname_parts) + ".png"
402422
os.makedirs(PERFORMANCE_PLOTS_DIR_PATH, exist_ok=True)
403423
fpath = os.path.join(PERFORMANCE_PLOTS_DIR_PATH, fname)

openadapt/record.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@
4747

4848
EVENT_TYPES = ("screen", "action", "window")
4949
LOG_LEVEL = "INFO"
50+
# whether to write events of each type in a separate process
5051
PROC_WRITE_BY_EVENT_TYPE = {
5152
"screen": True,
53+
"screen/video": True,
5254
"action": True,
5355
"window": True,
5456
}
@@ -183,8 +185,15 @@ def process_events(
183185
# XXX TODO: mitigate
184186
if event.type == "screen":
185187
prev_screen_event = event
186-
if config.RECORD_VIDEO and config.RECORD_FULL_VIDEO:
187-
video_write_q.put(event)
188+
if config.RECORD_FULL_VIDEO:
189+
video_event = event._replace(type="screen/video")
190+
process_event(
191+
video_event,
192+
video_write_q,
193+
write_video_event,
194+
recording,
195+
perf_q,
196+
)
188197
num_video_events.value += 1
189198
elif event.type == "window":
190199
prev_window_event = event
@@ -216,7 +225,14 @@ def process_events(
216225
num_screen_events.value += 1
217226
prev_saved_screen_timestamp = prev_screen_event.timestamp
218227
if config.RECORD_VIDEO and not config.RECORD_FULL_VIDEO:
219-
video_write_q.put(prev_screen_event)
228+
prev_video_event = prev_screen_event._replace(type="screen/video")
229+
process_event(
230+
prev_video_event,
231+
video_write_q,
232+
write_video_event,
233+
recording,
234+
perf_q,
235+
)
220236
num_video_events.value += 1
221237
if prev_saved_window_timestamp < prev_window_event.timestamp:
222238
process_event(
@@ -462,6 +478,7 @@ def write_video_event(
462478
Returns:
463479
dict containing state.
464480
"""
481+
assert event.type == "screen/video"
465482
screenshot_image = event.data
466483
screenshot_timestamp = event.timestamp
467484
force_key_frame = last_pts == 0
@@ -479,7 +496,7 @@ def write_video_event(
479496
last_pts,
480497
force_key_frame,
481498
)
482-
perf_q.put((f"{event.type}(video)", event.timestamp, utils.get_timestamp()))
499+
perf_q.put((event.type, event.timestamp, utils.get_timestamp()))
483500
return {
484501
**kwargs,
485502
**{
@@ -1266,7 +1283,7 @@ def record(
12661283
video_writer = multiprocessing.Process(
12671284
target=write_events,
12681285
args=(
1269-
"screen",
1286+
"screen/video",
12701287
write_video_event,
12711288
video_write_q,
12721289
num_video_events,

0 commit comments

Comments
 (0)