feat(adapter): scaffold balabit data adapter package

This commit is contained in:
2026-05-10 12:29:11 +08:00
parent 4d414fd180
commit 97a835769e
3 changed files with 116 additions and 0 deletions

View File

@@ -0,0 +1 @@
"""Data adapters: convert external datasets to the project's traces.jsonl format."""

View File

@@ -0,0 +1,79 @@
"""Adapter for the Balabit Mouse Dynamics Challenge dataset.
Source: https://github.com/balabit/Mouse-Dynamics-Challenge
Each session is a CSV file with columns:
record timestamp, client timestamp, button, state, x, y
Where:
state ∈ {Move, Pressed, Released, Drag, Scroll}
button ∈ {NoButton, Left, Right, Wheel}
We extract "click-anchored" trajectory segments: each Pressed event
defines a target, and the W ms of Move events preceding it form one
training trace.
"""
from __future__ import annotations
import json
import logging
from dataclasses import dataclass
from pathlib import Path
logger = logging.getLogger(__name__)
@dataclass
class MouseEvent:
"""A single mouse event from a Balabit CSV row."""
t_ms: int # client timestamp in milliseconds (relative to session start)
button: str # "NoButton", "Left", "Right", "Wheel"
state: str # "Move", "Pressed", "Released", "Drag", "Scroll"
x: int
y: int
@dataclass
class Segment:
"""A click-anchored trajectory segment ready to be written to JSONL."""
events: list[MouseEvent] # only Move events, sorted by t_ms ascending
click_x: int # the Pressed event's x coordinate
click_y: int # the Pressed event's y coordinate
click_t_ms: int # the Pressed event's timestamp
session_id: str # e.g. "user7_session_42"
def parse_session_csv(path: Path) -> list[MouseEvent]:
"""Stub — implemented in Task 3."""
raise NotImplementedError
def segment_by_clicks(
events: list[MouseEvent],
window_ms: int,
session_id: str,
) -> list[Segment]:
"""Stub — implemented in Task 4."""
raise NotImplementedError
def filter_segments(
segments: list[Segment],
min_events: int,
min_dist: int,
max_span_ms: int,
max_gap_ms: int,
) -> list[Segment]:
"""Stub — implemented in Task 5."""
raise NotImplementedError
def process_session(
csv_path: Path,
output_jsonl: Path,
config,
) -> int:
"""Stub — implemented in Task 6."""
raise NotImplementedError

View File

@@ -0,0 +1,36 @@
"""Tests for Balabit Mouse Dynamics Challenge data adapter."""
from __future__ import annotations
from pathlib import Path
import pytest
def test_module_exports():
"""The adapter module must export the public functions used by CLI."""
from ai_mouse.data_adapters import balabit
assert hasattr(balabit, "parse_session_csv")
assert hasattr(balabit, "segment_by_clicks")
assert hasattr(balabit, "filter_segments")
assert hasattr(balabit, "process_session")
assert hasattr(balabit, "MouseEvent")
assert hasattr(balabit, "Segment")
def test_mouse_event_dataclass():
"""MouseEvent has expected fields."""
from ai_mouse.data_adapters.balabit import MouseEvent
e = MouseEvent(t_ms=100, button="NoButton", state="Move", x=300, y=400)
assert e.t_ms == 100
assert e.state == "Move"
assert e.x == 300
def test_segment_dataclass():
"""Segment has expected fields."""
from ai_mouse.data_adapters.balabit import MouseEvent, Segment
events = [MouseEvent(t_ms=0, button="NoButton", state="Move", x=10, y=20)]
s = Segment(events=events, click_x=100, click_y=200, click_t_ms=500, session_id="user1_s1")
assert s.events == events
assert s.click_x == 100
assert s.session_id == "user1_s1"