Skip to content

Commit

Permalink
Merge branch 'silence-warnings' into add-GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
0dm committed May 29, 2023
2 parents bfd4af7 + b534bc4 commit 33f8cc9
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 62 deletions.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,11 @@ cache
# db
*.db

# VSCode
.VSCode

# Generated performance charts
performance

# Generated when adding editable dependencies in requirements.txt (-e)
src
src
36 changes: 36 additions & 0 deletions alembic/versions/104d4a614d95_add_performancestat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""add PerformanceStat
Revision ID: 104d4a614d95
Revises: b2dc41850120
Create Date: 2023-05-27 02:59:14.032373
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '104d4a614d95'
down_revision = 'b2dc41850120'
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('performance_stat',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('recording_timestamp', sa.Integer(), nullable=True),
sa.Column('event_type', sa.String(), nullable=True),
sa.Column('start_time', sa.Integer(), nullable=True),
sa.Column('end_time', sa.Integer(), nullable=True),
sa.Column('window_id', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id', name=op.f('pk_performance_stat'))
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('performance_stat')
# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion openadapt/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"CACHE_VERBOSITY": 0,
"DB_ECHO": False,
"DB_FNAME": "openadapt.db",
"OPENAI_API_KEY": None,
"OPENAI_API_KEY": "<set your api key in .env>",
#"OPENAI_MODEL_NAME": "gpt-4",
"OPENAI_MODEL_NAME": "gpt-3.5-turbo",
# may incur significant performance penalty
Expand Down
35 changes: 33 additions & 2 deletions openadapt/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
import sqlalchemy as sa

from openadapt.db import Session
from openadapt.models import ActionEvent, Screenshot, Recording, WindowEvent
from openadapt.models import (
ActionEvent,
Screenshot,
Recording,
WindowEvent,
PerformanceStat,
)


BATCH_SIZE = 1
Expand All @@ -11,7 +17,7 @@
action_events = []
screenshots = []
window_events = []

performance_stats = []

def _insert(event_data, table, buffer=None):
"""Insert using Core API for improved performance (no rows are returned)"""
Expand Down Expand Up @@ -68,6 +74,31 @@ def insert_window_event(recording_timestamp, event_timestamp, event_data):
}
_insert(event_data, WindowEvent, window_events)

def insert_perf_stat(recording_timestamp, event_type, start_time, end_time):
"""
Insert event performance stat into db
"""

event_perf_stat = {
"recording_timestamp": recording_timestamp,
"event_type": event_type,
"start_time": start_time,
"end_time": end_time,
}
_insert(event_perf_stat, PerformanceStat, performance_stats)

def get_perf_stats(recording_timestamp):
"""
return performance stats for a given recording
"""

return (
db
.query(PerformanceStat)
.filter(PerformanceStat.recording_timestamp == recording_timestamp)
.order_by(PerformanceStat.start_time)
.all()
)

def insert_recording(recording_data):
db_obj = Recording(**recording_data)
Expand Down
11 changes: 11 additions & 0 deletions openadapt/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,14 @@ class WindowEvent(db.Base):
@classmethod
def get_active_window_event(cls):
return WindowEvent(**window.get_active_window_data())


class PerformanceStat(db.Base):
__tablename__ = "performance_stat"

id = sa.Column(sa.Integer, primary_key=True)
recording_timestamp = sa.Column(sa.Integer)
event_type = sa.Column(sa.String)
start_time = sa.Column(sa.Integer)
end_time = sa.Column(sa.Integer)
window_id = sa.Column(sa.String)
92 changes: 38 additions & 54 deletions openadapt/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"""

from collections import Counter, defaultdict, namedtuple
from datetime import datetime
from collections import namedtuple
from functools import partial
from typing import Any, Callable, Dict
import multiprocessing
Expand All @@ -17,12 +16,10 @@
import sys
import threading
import time
import zlib

from loguru import logger
from pynput import keyboard, mouse
import fire
import matplotlib.pyplot as plt
import mss.tools

from openadapt import config, crud, utils, window
Expand All @@ -35,7 +32,6 @@
"action": True,
"window": True,
}
DIRNAME_PERFORMANCE_PLOTS = "performance"
PLOT_PERFORMANCE = False


Expand Down Expand Up @@ -406,60 +402,35 @@ def read_window_events(
prev_window_data = window_data


def plot_performance(
recording_timestamp: float,
def performance_stats_writer (
perf_q: multiprocessing.Queue,
) -> None:
recording_timestamp: float,
terminate_event: multiprocessing.Event,
):
"""
Plot the performance of the event processing and writing.
Write performance stats to the db.
Each entry includes the event type, start time and end time
Args:
perf_q: A queue for collecting performance data.
recording_timestamp: The timestamp of the recording.
perf_q: A queue with performance data.
terminate_event: An event to signal the termination of the process.
"""

type_to_prev_start_time = defaultdict(list)
type_to_start_time_deltas = defaultdict(list)
type_to_proc_times = defaultdict(list)
type_to_count = Counter()
type_to_timestamps = defaultdict(list)
while not perf_q.empty():
event_type, start_time, end_time = perf_q.get()
prev_start_time = type_to_prev_start_time.get(event_type, start_time)
start_time_delta = start_time - prev_start_time
type_to_start_time_deltas[event_type].append(start_time_delta)
type_to_prev_start_time[event_type] = start_time
type_to_proc_times[event_type].append(end_time - start_time)
type_to_count[event_type] += 1
type_to_timestamps[event_type].append(start_time)

if not PLOT_PERFORMANCE:
return

y_data = {"proc_times": {}, "start_time_deltas": {}}
for i, event_type in enumerate(type_to_count):
type_count = type_to_count[event_type]
start_time_deltas = type_to_start_time_deltas[event_type]
proc_times = type_to_proc_times[event_type]
y_data["proc_times"][event_type] = proc_times
y_data["start_time_deltas"][event_type] = start_time_deltas

fig, axes = plt.subplots(2, 1, sharex=True, figsize=(20,10))
for i, data_type in enumerate(y_data):
for event_type in y_data[data_type]:
x = type_to_timestamps[event_type]
y = y_data[data_type][event_type]
axes[i].scatter(x, y, label=event_type)
axes[i].set_title(data_type)
axes[i].legend()
# TODO: add PROC_WRITE_BY_EVENT_TYPE
fname_parts = ["performance", f"{recording_timestamp}"]
fname = "-".join(fname_parts) + ".png"
os.makedirs(DIRNAME_PERFORMANCE_PLOTS, exist_ok=True)
fpath = os.path.join(DIRNAME_PERFORMANCE_PLOTS, fname)
logger.info(f"{fpath=}")
plt.savefig(fpath)
os.system(f"open {fpath}")
utils.configure_logging(logger, LOG_LEVEL)
utils.set_start_time(recording_timestamp)
logger.info("performance stats writer starting")
signal.signal(signal.SIGINT, signal.SIG_IGN)
while not terminate_event.is_set() or not perf_q.empty():
try:
event_type, start_time, end_time = perf_q.get_nowait()
except queue.Empty:
continue

crud.insert_perf_stat(
recording_timestamp, event_type, start_time, end_time,
)
logger.info("performance stats writer done")


def create_recording(
Expand Down Expand Up @@ -644,6 +615,17 @@ def record(
)
window_event_writer.start()

terminate_perf_event = multiprocessing.Event()
perf_stat_writer = multiprocessing.Process(
target=performance_stats_writer,
args=(
perf_q,
recording_timestamp,
terminate_perf_event,
),
)
perf_stat_writer.start()

# TODO: discard events until everything is ready

try:
Expand All @@ -662,10 +644,12 @@ def record(
action_event_writer.join()
window_event_writer.join()

plot_performance(recording_timestamp, perf_q)
terminate_perf_event.set()

logger.info(f"saved {recording_timestamp=}")
if PLOT_PERFORMANCE:
utils.plot_performance(recording_timestamp)

logger.info(f"saved {recording_timestamp=}")

if __name__ == "__main__":
fire.Fire(record)
Loading

0 comments on commit 33f8cc9

Please sign in to comment.