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

Task specific params and Matrix Layout #349

Merged
merged 6 commits into from
Sep 23, 2024

Conversation

tab-cmd
Copy link
Contributor

@tab-cmd tab-cmd commented Sep 19, 2024

Overview

Updated parameters.json and downstream code to pull Task-relevant parameters. I've updated the code where applicable and added a layout option for Matrix.

Ticket

https://www.pivotaltracker.com/story/show/188172524
https://www.pivotaltracker.com/story/show/188188217

Contributions

  • parameters.json: split matrix and rsvp parameters. Add layout.
  • MatrixDisplay: add a method to set the layout of the grid

Test

  • make test-all
  • Ran with fake data through the client

Documentation

  • Are documentation updates required? In-line, README, or documentation? docstrings and other docs updated.

Changelog

  • Is the CHANGELOG.md updated with your detailed changes? partial

Separate matrix / rsvp parameters. Add a parameter for layout (pass other relevant params; space character etc.)
row3 = f" ZXCV{space}BNM "
return f"{row1}{row2}{row3}".index
row3 = f"ZXCV{space}BNM"
return f"{row1}{row2}{row3}"
Copy link
Collaborator

@lawhead lawhead Sep 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was being used as a function to select a position on a grid and the spaces were important. For example, if I want to represent the first 10 letters (A through J) on two rows (2x6) with the items on the second row centered, the grid positions would be:

. . . . . .
. . . . . .

with this arrangement:

A  B  C  D  E  F
.  G  H  I  J .

I would represent this as:

def my_order():
  row1="ABCDEF"
  row2=" GHIJ "
  return f"{row1}{row2}".index

>>> sort_fn = my_order()
>>> sort_fn("G")
7

Note that this is different than:

>>> "ABCDEFGHIJ".index("G")
6

So this does need to return a function, not just a list of symbols. This could have been better documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you're saying. I'll refactor to use sort_order from the stimuli properties. The Matrix Display configuration could use some refactoring. We might remove row/col controls altogether and allow keyboard selections only.

self.stimuli_inquiry = []
self.stimuli_timing = []
self.stimuli_colors = []
self.stimuli_font = stimuli.stim_font

assert stimuli.is_txt_stim, "Matrix display is a text only display"

self.symbol_set = symbol_set or alphabet()
self.symbol_set = self.build_symbol_set(stimuli)
self.sort_order = sort_order or self.symbol_set.index
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned elsewhere we do need to separate out the concept of a symbol set from how these symbols are positioned in the grid. I used the term sort_order here but that's a bit misleading, since it's not just re-ordering symbols but it's choosing a position on the grid. The symbol_set code should not change, it's the sort_order (or something better named, like symbol_positioner?) that should be set.

return qwerty_order()
elif stimuli.layout == 'FREQ':
return frequency_order()
else:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense for stimuli.layout to be a Callable that could be used directly?

   def init_symbol_positioner(self, stimuli: StimuliProperties) -> List[str]:
        """Build the symbol set for the display."""
        assert stimuli.layout, "Layout is required"
        return stimuli.layout()

It would probably be good to separate out the mapping of the string to the function into another place. An enum would be a good option here since it would also provide a discrete set of values.

def alphabet_order(parameters=None, include_path=True):
  return alphabet(parameters, include_path).index


class MatrixLayout(Enum):
    ALP = ('alp', alphabet_order)
    QWERTY = ('qwerty', qwerty_order)
    FREQ = ('freq', frequency_order)

    def __new__(cls, *args):
      """Autoincrements the value of each item added to the enum."""
      value = len(cls.__members__) + 1
      obj = object.__new__(cls)
      obj._value_ = value
      return obj

    def __init__(self, label, fn):
      self.label = label
      self.symbol_positioner = fn

Initialized from parameters.json:

layout = MatrixLayout[parameters.get('matrix_keyboard_layout', 'ALP')]
StimuliProperties(layout=layout.symbol_positioner)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing these concepts is a little confusing right now: a symbol set and an "order" that redefines the alphabet symbol set. What we want with QWERTY is to define positions and order, but the rows/cols parameters also control this, so in effect, it adds some padding values to the grid and enforces the order. We may want to separate the concepts of Keyboard, Symbol Set, and Ordering. I'm content with the hacky approach I pushed for now since we have other tickets to complete before the next experiment begins, but happy to discuss more before we release 2.0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it would be useful to have a higher level interface/API for interacting with these items. I like the idea of passing in a Keyboard, for instance. This would encapsulate the concepts of rows, columns, ordering, spacing. However, I think the underlying low level mechanism will be the same.

@tab-cmd tab-cmd merged commit e4d3248 into session-orchestrator Sep 23, 2024
6 checks passed
@tab-cmd tab-cmd deleted the task_specific_params branch September 23, 2024 20:45
tab-cmd added a commit that referenced this pull request Oct 23, 2024
* gets `main.py` functionality working in the orchestrator

* Fixes task initialization process

Save folder and parameters are now saved and loaded per task as they are executed rather than once per session.

* Moves parameter loading to task execution

* Adds functions for parsing visit actions

* Adds some test to action parsing

* Adds tests for action serialization

* Adds parameter validation function with tests

* Updates changelog.md

* lints actions.py and test_actions.py

* Adds constant for action separator

* removes old file that has been moved

* renames action parsing module and updates test imports

* simplifies `serialize_actions` function

* Fixes typing and formatting issues

* removes `parse_actions.py`

* renames `parse_actions` to `parse_sequence`

* replaces magic '->' string with constant

* removes unnecessary error check

* Adds basic structure for orchestrator actions

* refactors config function naming and adds support for actions in parsing functions

* renames `test_actions` to `test_config`

* Adds additional tests for action parsing

* Adds action serialization tests

* Add more actions

* Refactors type unions to be `Optional`s

* Adds parameter loading to the orchestrator

* Adds initialize and cleanup methods to `Task`

* makes `name` a property in `OfflineAnalysisAction`

* Implements `setup` method in base `Task` class

* Adds missing parent `execute` call

* changes `initialize` call to `setup` in orchestrator

* Refactors `name` to be a property in base `Task` class

* Refactors `config.py` to only work with tasks

* adds demo file for orchestrator execution

* Fixes data_save initialization in orchestrator

* Creates default_user_id constant

* Fixes incorrect user initialization

* Refactors orchestrator save structure

* Implements `save` method

* Adds `task_registry_dict` to simplify fetching classes for initialization

This is currently redundant with the `TaskType` approach and ideally would be refactored to have only one source of string reference

* Adds comment about `task_registry_dict`

* Moves `task_registry_dict` to `orchestrator.config`

* refactors `name` property of tasks

* Adds task registry class

* Refactor `super` calls in orchestrator actions

* adds type hint for task parameter

* calls `construct_command` in constructor

* adds function to run experiment data collection without running from the command line

* adds `ExperimentFieldCollectionAction`

* refactors orchestrator demo file to use `ExperimentFieldCollectionAction`

* lints `task_registry.py`

* imports `Task` for correct typing in config

* Updates `task_registry.py` to import all tasks

* moves `TASK_SEPERATOR` to `config.py`

* Adds docstring to orchestrator

* Renames `TestConfig`

* removes property decorator in task class

* removes implementation of abstract class

* updates changelog

* removes super calls to abstract method `execute`

* Filters out abstract subclasses from registry generation

* removes registry dict empty initialization

* Fixes typing issues

* Removes task import from the registry

* Refactors config to support TaskRegistry

* Refactors task names

* Refactors `TaskRegistry` to recursively collect subclasses

This makes it compatible with the new `BaseCalibrationTask` as it will be able to collect grandchild classes of `Task` (which is all calibration tasks now)

* Makes `BaseCalibrationTask` abstract

* removes name and label from `timing_verification.py`

* imports tasks in their module's `__init__.py`

This makes all tasks available to the task registry when it is initialized.

* Enforces `name` attribute in base task class

* adds calibration task check to the registry. TaskType now no longer exists

* fixes incorrect method name in TaskRegistry

* updates bci_main_tests to call main methods properly

* updates method calls in protocol parsing tests

* refactors `start_app` to use `start_experiment_field_collection_gui`

* changes orchestrator directory name to work on windows

* Adds basic selection box for orchestrator protocol

* Adds the ability to add and remove tasks to the protocol

* renames `sequence` to `protocol` in orchestrator config

* Allows protocol to be saved to `experiments.json`

* adds move up and down controls to tasks in protocol

* Refactors scrollable frames to have a title label. Some buttons are still misaligned in the ExperimentRegistry

* Moves create experiment button to the bottom of the gui

* changes task and action names to be more consistent

* adds basic `TaskData` return object

* fixes failing test in `test_copy_phrase`

* fixes missing return statement for `construct_command`

* adds `task_dict` to `TaskData`

* updates changelog

* adds seperate BCIUI file with stylsheet

This will hopefully replace BCIGui later, but I am keeping it seperate for now as to not break existing uis on this branch

* update demo experiment layout

* adjust stylesheet to remove label backgrounds

* improves gui code to allow for editing fields

* add basic removeable element

* add dynamic list class for easy ui reactivity

* Add small button component

* removes explicit color from small button

* adds basic toggle class

* changes toggle componenet to be simple connecting function

* makes on off callbacks optional in `make_toggle`

* add toggle to optional button

* swaps confusing parameter names

* loads experiment fields into combobox

* adds field buttons

* refactors gui to be inherited class
This improves state handling, as variables and widgets can be stored on the class itself

* add simple `show_alert` method

* add show_alert method

* adds validation to added fields


* adds experiment creation to application

* rename `gui_rewrite` to `bciui`

* adds app start method

* adds vertical center option to bciui

* add the beginning of intertask action ui

* add progress text

* allows for additional args in app running

* add functional progress bar

* Add next task display and functioning buttons

* adds seperate BCIUI file with stylesheet

This will hopefully replace BCIGui later, but I am keeping it seperate for now as to not break existing uis on this branch

* update demo experiment layout

* adjust stylesheet to remove label backgrounds

* improves gui code to allow for editing fields

* add basic removeable element

* add dynamic list class for easy ui reactivity

* Add small button component

* removes explicit color from small button

* adds basic toggle class

* changes toggle componenet to be simple connecting function

* makes on off callbacks optional in `make_toggle`

* add toggle to optional button

* swaps confusing parameter names

* loads experiment fields into combobox

* adds field buttons

* refactors gui to be inherited class
This improves state handling, as variables and widgets can be stored on the class itself

* add simple `show_alert` method

* add show_alert method

* adds validation to added fields

* remove debug prints

* adds experiment creation to application

* remove unused code

* rename `gui_rewrite` to `bciui`

* adds app start method

* adds vertical center option to bciui

* add beginning of intertask action ui

* add progress text

* allows for additional args in app running

* add functional progress bar

* Add next task display and functioning buttons

* add protocol layout and fix bug in DynamicList

* Parameter guardrails and cleanup (#340)

* supports adding tasks to the interface

* add reordering to tasks in the protocol

* allows adding new fields through the ui

* adds saving protocol in experiment from gui

* Task initialization and cleanup (#343)

* Adds documentation to ui methods

* rename `intertask_action` to `intertask_gui`

* add action that displays intertask GUI

* Add proper types and docs to bciui

* Deprecates old ExperimentRegistry in favor of new ui code

* Fixes incorrect background color when using dark mode

* improve stylsheet loading to use existing bcipy constants

* update legacy BCInterface to base session-orchestrator

* Session Orchestrator Updates [Post-Testing] (#344)

* Add logger to each task run

* Bcipy client refactor (#346)

* Task specific params and Matrix Layout (#349)

* Orchestrator - Load a JSON of copy phrases to use during execution  (#352)

* fix logger assignment

* BciPy report action, offline analysis updates,  Intertask GUI. (#354)

* updates to include IP changes

* Update to get static offset from the device


---------

Co-authored-by: Carson Reader <[email protected]>
Co-authored-by: lawhead <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants