Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(api, performance-metrics): clean up performance-metrics tracking #15289

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: Add storing analysis context on track
  • Loading branch information
DerekMaggio committed May 30, 2024
commit 53fe590d2ca084217600cc2904db90d1beeb39b3
24 changes: 20 additions & 4 deletions api/src/opentrons/util/performance_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Performance helpers for tracking robot context."""

import functools
from pathlib import Path
from opentrons_shared_data.performance.dev_types import (
SupportsTracking,
Expand All @@ -18,6 +19,7 @@
_should_track = ff.enable_performance_metrics(
RobotTypeEnum.robot_literal_to_enum(robot_configs.load().model)
)
STORE_EACH = _should_track


class StubbedTracker(SupportsTracking):
Expand Down Expand Up @@ -70,7 +72,21 @@ def _get_robot_context_tracker() -> SupportsTracking:


def track_analysis(func: F) -> F:
"""Track the analysis of a protocol."""
return _get_robot_context_tracker().track(RobotContextState.ANALYZING_PROTOCOL)(
func
)
"""Track the analysis of a protocol and optionally store each run."""
# This will probably not stick around very long but it gives me
# the ability to test this on a robot

# Typing a decorator that wraps a decorator with args, nope
@functools.wraps(func)
def wrapper(*args, **kwargs): # type: ignore # noqa: ANN002, ANN003, ANN201
tracker = _get_robot_context_tracker()
tracked_func = tracker.track(RobotContextState.ANALYZING_PROTOCOL)(func)

result = tracked_func(*args, **kwargs)

if STORE_EACH:
tracker.store()

return result

return wrapper # type: ignore
59 changes: 48 additions & 11 deletions api/tests/opentrons/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ def override_data_store(tmp_path: Path) -> Iterator[None]:
context_tracker._store = old_store # type: ignore[attr-defined]


@pytest.fixture
def monkeypatch_set_store_each_to_true(monkeypatch: pytest.MonkeyPatch) -> None:
"""Override the STORE_EACH flag for the RobotContextTracker."""
monkeypatch.setattr("opentrons.util.performance_helpers.STORE_EACH", True)


def verify_metrics_store_file(file_path: Path, expected_length: int) -> None:
"""Verify that the metrics store file contains the expected number of lines."""
with open(file_path, "r") as f:
stored_data = f.readlines()
stored_data = [line.strip() for line in stored_data if line.strip()]
assert len(stored_data) == expected_length
for line in stored_data:
state_id, start_time, duration = line.strip().split(",")
assert state_id.isdigit()
assert state_id == str(RobotContextState.ANALYZING_PROTOCOL.state_id)
assert start_time.isdigit()
assert duration.isdigit()


def _list_fixtures(version: int) -> Iterator[Path]:
return Path(__file__).parent.glob(
f"../../../../shared-data/protocol/fixtures/{version}/*.json"
Expand Down Expand Up @@ -309,17 +329,34 @@ def run(protocol):

assert len(store._data) == num_storage_entities_before_analysis + 1

with open(store.metadata.data_file_location, "r") as f:
stored_data = f.readlines()
assert len(stored_data) == 0
verify_metrics_store_file(store.metadata.data_file_location, 0)

context_tracker.store()

with open(store.metadata.data_file_location, "r") as f:
stored_data = f.readlines()
stored_data = [line.strip() for line in stored_data if line.strip()]
assert len(stored_data) == 1
state_id, start_time, duration = stored_data[0].strip().split(",")
assert state_id == str(RobotContextState.ANALYZING_PROTOCOL.state_id)
assert start_time.isdigit()
assert duration.isdigit()
verify_metrics_store_file(store.metadata.data_file_location, 1)


@pytest.mark.usefixtures("override_data_store", "monkeypatch_set_store_each_to_true")
def test_track_analysis_with_store_each_run(tmp_path: Path) -> None:
"""Test that the RobotContextTracker tracks analysis and stores each run."""
protocol_source = textwrap.dedent(
"""
requirements = {"apiLevel": "2.15"}

def run(protocol):
pass
"""
)
protocol_source_file = tmp_path / "protocol.py"
protocol_source_file.write_text(protocol_source, encoding="utf-8")
store = context_tracker._store # type: ignore[attr-defined]

verify_metrics_store_file(store.metadata.data_file_location, 0)

_get_analysis_result([protocol_source_file])

verify_metrics_store_file(store.metadata.data_file_location, 1)

_get_analysis_result([protocol_source_file])

verify_metrics_store_file(store.metadata.data_file_location, 2)