Purpose: Explicit specifications for every interface. When a method is called, what must be true before (preconditions), what is guaranteed after (postconditions), and what can go wrong (errors).
Each contract specifies:
- Purpose: What this method does
- Preconditions: What must be true when called
- Postconditions: What is guaranteed upon return
- Parameters: Types and constraints
- Returns: Type and meaning
- Errors: Exceptions that can be raised
- Performance: Expected timing
Purpose: Retrieve current state of a single event.
Preconditions:
0 <= event_idx < len(events)
Postconditions:
- Returns current event dict (may differ from baseline if modified)
- Does not modify any state
Parameters:
event_idx: Zero-based event index
Returns:
dictwith keys:{t, v, r, f, a, S, gamma_self}
Errors:
IndexErrorifevent_idxout of range
Performance: O(1), <1ms
Purpose: Retrieve all current events.
Preconditions: None
Postconditions:
- Returns copy of events list (modifications won't affect Model)
- Does not modify any state
Returns:
list[dict]with all events
Errors: None
Performance: O(n), <10ms for n=1000
Purpose: Check if primitive has been modified from baseline.
Preconditions:
0 <= event_idx < len(events)prim in ['v', 'r', 'f', 'a', 'S']
Postconditions:
- Returns True if current value differs from baseline
- Does not modify any state
Parameters:
event_idx: Zero-based event indexprim: Primitive name
Returns:
bool: True if modified, False if at baseline
Errors:
IndexErrorifevent_idxout of rangeKeyErrorifpriminvalid
Performance: O(1), <1ms
Purpose: Get the original (pre-edit) value of a primitive.
Preconditions:
0 <= event_idx < len(events)prim in ['v', 'r', 'f', 'a', 'S']
Postconditions:
- Returns baseline value
- Does not modify any state
Parameters:
event_idx: Zero-based event indexprim: Primitive name
Returns:
float: Original value from file load or last save
Errors:
IndexErrorifevent_idxout of rangeKeyErrorifpriminvalid
Performance: O(1), <1ms
Purpose: Update one or more primitives for an event.
Preconditions:
0 <= event_idx < len(events)changeskeys are subset of['v', 'r', 'f', 'a', 'S']changesvalues are valid floats for those primitives
Postconditions:
events[event_idx]updated with changes- Modified primitives added to
modified_events[event_idx] - If value equals baseline, removed from modified set
Parameters:
event_idx: Zero-based event indexchanges:dictmapping primitive names to new values
Returns: None
Errors:
IndexErrorifevent_idxout of rangeKeyErrorifchangescontains invalid primitive nameValueErrorif values are invalid (e.g., negative for magnitude primitives)
Performance: O(k) where k = len(changes), <1ms typical
Example:
model.update_event(5, {'r': 0.85, 'f': 0.3})
# Updates event 5's r and f primitivesPurpose: Reset single primitive to baseline value.
Preconditions:
0 <= event_idx < len(events)prim in ['v', 'r', 'f', 'a', 'S']
Postconditions:
events[event_idx][prim]set tobaseline_events[event_idx][prim]primremoved frommodified_events[event_idx]- Returns the baseline value
Parameters:
event_idx: Zero-based event indexprim: Primitive name
Returns:
float: The baseline value that was restored
Errors:
IndexErrorifevent_idxout of rangeKeyErrorifpriminvalid
Performance: O(1), <1ms
Purpose: Get indices of all events with modifications.
Preconditions: None
Postconditions:
- Returns set of event indices with at least one modified primitive
- Does not modify any state
Returns:
set[int]: Event indices with modifications
Errors: None
Performance: O(n) where n = number of events, <10ms for n=1000
Purpose: Update baseline after save (makes current state the new ground truth).
Preconditions:
eventshas same structure as current events
Postconditions:
baseline_eventsupdated to matcheventsmodified_eventscleared (all events now at baseline)
Parameters:
events: New baseline state
Returns: None
Errors:
ValueErrorifeventsstructure invalid
Performance: O(n), <10ms for n=1000
Purpose: Handle user editing a primitive value.
Preconditions:
- Valid event_idx, prim, value (validated by caller)
Postconditions:
- Model updated with new value
- Primitive panel marker updated visually
- Trajectory panel updated if gamma_self affected
Parameters:
event_idx: Event being editedprim: Primitive namevalue: New value
Returns: None
Errors: None (errors logged, not raised)
Performance: <50ms (target), currently 200-500ms ❌
Information Flow:
Controller → Model.update_event()
Controller → Model.is_modified()
Controller → PrimitivePanel.update_marker()
Controller → TrajectoryPanel.update_trajectory()
Purpose: Handle user resetting primitive to baseline.
Preconditions:
- Valid event_idx, prim
Postconditions:
- Model primitive reset to baseline
- Primitive panel marker updated to show baseline value
- Marker visual shows not-modified state
- Trajectory panel updated if gamma_self affected
Parameters:
event_idx: Event being resetprim: Primitive name
Returns: None
Errors: None (errors logged, not raised)
Performance: <50ms (target)
Information Flow:
Controller → Model.get_baseline_value()
Controller → Model.update_event()
Controller → PrimitivePanel.update_marker()
Controller → TrajectoryPanel.update_trajectory()
Purpose: Save current state to file.
Preconditions: None
Postconditions:
- If no modifications: returns False, no action
- If modifications: writes to file, updates baseline, clears modified markers
- All panels show unmodified state
Returns:
bool: True if saved, False if no changes
Errors:
IOErrorif file write fails (caught and logged)
Performance: O(n), <100ms for n=1000
Information Flow:
Controller → Model.get_modified_events()
Controller → Model.get_all_events()
Controller → save_to_csv()
Controller → Model.set_baseline()
Controller → PrimitivePanel.clear_all_modified()
Purpose: Handle user selecting an event.
Preconditions:
- Valid event_idx
Postconditions:
- Selection state updated in Controller
- All panels visually show selected event
Parameters:
event_idx: Event being selected
Returns: None
Errors: None
Performance: <10ms (target)
Information Flow:
Controller → self.selected_event = event_idx
Controller → PrimitivePanel.set_selection()
Controller → TrajectoryPanel.set_selection()
Purpose: Full rebuild of all markers (rare, only on initialization).
Preconditions:
- Model has valid events
Postconditions:
- All subplots cleared and redrawn
- All DraggablePoint objects recreated
_markersdict populated
Returns: None
Errors: None (errors logged)
Performance: O(n * m) where n=events, m=primitives, ~10ms per marker
- Current: 200-500ms for 50 markers ❌
- Target: <500ms for 50 markers (acceptable for rare operation)
Usage: Only call on initialization or major structural change (e.g., file reload)
Purpose: Update single marker incrementally.
Preconditions:
- Marker exists in
_markers[(event_idx, prim)] - Valid value for primitive
Postconditions:
- Marker position updated to reflect value
- Marker visual updated to show modified/unmodified state
- Canvas redrawn (only affected region)
Parameters:
event_idx: Event indexprim: Primitive namevalue: New value to displayis_modified: Whether to show as modified from baseline
Returns: None
Errors: None (errors logged)
Performance: <50ms (target)
Implementation:
def update_marker(self, event_idx, prim, value, is_modified):
marker = self._markers[(event_idx, prim)]
marker.update_position(value)
marker.set_modified(is_modified)
self.canvas.draw_idle() # Efficient redrawPurpose: Clear modified visual state from all markers (after save).
Preconditions: None
Postconditions:
- All markers show unmodified visual state
- Positions unchanged
Returns: None
Errors: None
Performance: O(n * m), <100ms for 1000 events
Purpose: Visually highlight selected event.
Preconditions:
- Valid event_idx
Postconditions:
- Previous selection unhighlighted
- New selection highlighted
Returns: None
Errors: None
Performance: <10ms (target)
Purpose: Move marker to new position on plot.
Preconditions:
- Valid value for this primitive's subplot
Postconditions:
- Marker moved to (event_idx, value) in data coordinates
- Artist updated
Parameters:
value: New y-coordinate value
Returns: None
Errors: None
Performance: <1ms
Purpose: Update visual to show modified/unmodified state.
Preconditions: None
Postconditions:
- Marker color/style reflects modification state
- Artist updated
Parameters:
is_modified: True for modified style, False for baseline style
Returns: None
Errors: None
Performance: <1ms
Purpose: Reset internal double-click detection state machine.
Preconditions: None
Postconditions:
_click_countreset to 0- State machine back to idle
Returns: None
Errors: None
Performance: <1ms
Usage: Called after successful reset to prevent triple-click triggering another reset
Purpose: Replot gamma_self trajectory with current Model data.
Preconditions:
- Model has valid events with gamma_self values
Postconditions:
- Trajectory line updated
- Gauge updated to show current position
- Canvas redrawn
Returns: None
Errors: None (errors logged)
Performance: O(n), <50ms for n=1000 (target)
Purpose: Highlight selected event on trajectory.
Preconditions:
- Valid event_idx
Postconditions:
- Selection marker updated to event's gamma_self position
Returns: None
Errors: None
Performance: <10ms (target)
- Preconditions: Add assertions at method entry
def update_marker(self, event_idx, prim, value, is_modified):
assert 0 <= event_idx < len(self.model.events)
assert prim in ['v', 'r', 'f', 'a', 'S']
assert isinstance(value, (int, float))
assert isinstance(is_modified, bool)
# ... implementation ...- Postconditions: Add assertions before return
def update_event(self, event_idx, changes):
# ... implementation ...
assert self.events[event_idx][prim] == changes[prim]
if changes[prim] != self.baseline_events[event_idx][prim]:
assert prim in self.modified_events[event_idx]- Performance: Add timing instrumentation
import time
def update_marker(self, ...):
start = time.perf_counter()
# ... implementation ...
elapsed = time.perf_counter() - start
if elapsed > 0.050: # 50ms target
logger.warning(f"update_marker took {elapsed*1000:.1f}ms (target <50ms)")# WRONG: Silently ignoring errors
def update_marker(self, ...):
try:
marker = self._markers[(event_idx, prim)]
except KeyError:
return # ❌ Violates contract - should error# RIGHT: Fail loudly
def update_marker(self, ...):
marker = self._markers[(event_idx, prim)] # ✅ Raises KeyError# WRONG: Query modifies state
def get_event(self, idx):
self._last_accessed = idx # ❌ Side effect
return self.events[idx]# RIGHT: Query is pure
def get_event(self, idx):
return self.events[idx] # ✅ No side effects# WRONG: O(n) when contract specifies O(1)
def is_modified(self, idx, prim):
# ❌ Linear search violates O(1) contract
for i, modified_set in enumerate(self.modified_events):
if i == idx:
return prim in modified_set# RIGHT: O(1) as contracted
def is_modified(self, idx, prim):
return prim in self.modified_events[idx] # ✅ O(1)Last Updated: 2025-12-06
Status: Contracts defined for target architecture (not yet implemented)